View 如何对 SurfaceView 的方法调用做出反应?
How can a View react to a Method call of a SurfaceView?
继续
我正在编写吃豆人程序,它几乎完成了,但是我在显示得分和吃豆人当前方向的视图与绘制游戏的表面视图之间的通信方面遇到了问题。
PlayActivity(这是一个 AppCompatActivity)有一个 GameView(这是一个绘制游戏的 SurfaceView)并且知道 GameManager(我制作的一个 class 基本上知道 pacman,地图,幽灵,以及一切)。 PlayActivity 还有一个名为 scoreTv 的文本视图。我不知道如何更新这个文本视图。
想法是每当 GameManager 的方法 addScore(int),eatPallet(int, int), eatBonus(int,int) 时,scoreTv 都会更改其文本值; eatSuperPallet(int,int) 被调用。
这里是PlayActivity代码
package Activities;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.pacman.DBManager;
import Game.GameView;
import com.example.pacman.R;
import com.example.pacman.Score;
public class PlayActivity extends AppCompatActivity {
private TextView playerNickname;
TextView score;
private TextView maxScore;
private SurfaceView gameSurfaceView;
private GameView gameView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Modified code
setContentView(R.layout.activity_game);
//we get text view that we will use
playerNickname=(TextView) this.findViewById(R.id.tv_player);
score=(TextView) this.findViewById(R.id.tv_current_score);
maxScore=(TextView) this.findViewById(R.id.tv_current_max_score);
gameSurfaceView= (GameView) this.findViewById(R.id.game_view);
//set text view initial values
playerNickname.setText(getIntent().getExtras().getString("playerNickname"));
score.setText("0");
maxScore.setText("To modify");
this.gameView=new GameView(gameSurfaceView.getContext());
this.gameSurfaceView.getHolder().addCallback(this.gameView);
}
protected void onResume(){
super.onResume();
this.gameView.resume();
}
protected void onPause(){
super.onPause();
this.gameView.pause();
}
public void updateScore(int score){
this.score.setText(""+score);
}
public void onLose(double score){
//We try to save the score, if there is a previous register we write only if this score
//is better that the one before
DBManager manager;
long raw;
Score scoreToSave;
manager=new DBManager(this);
scoreToSave=new Score(this.playerNickname.toString(), score);
if(manager.saveScore(scoreToSave)==-1){
//if i couldn't save the score
if(manager.updateScore(scoreToSave)!=-1){
//if my new score is better than the one previous
}else{
//if my new score is worse or equal than the one previous
}
}
}
}
这是游戏视图代码
package Game;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import Activities.PlayActivity;
import Game.Character_package.Ghost;
import Game.Character_package.Pacman;
public class GameView extends SurfaceView implements Runnable, SurfaceHolder.Callback, GestureDetector.OnGestureListener {
private static final float SWIPE_THRESHOLD = 2;
private static final float SWIPE_VELOCITY = 2;
private boolean GHOST_INICIALIZED=false;
private GestureDetector gestureDetector;
private GameManager gameManager;
private Thread thread; //game thread
private SurfaceHolder holder;
private boolean canDraw = false;
private int blockSize; // Ancho de la pantalla, ancho del bloque
private static int movementFluencyLevel=8; //this movement should be a multiple of the blocksize and multiple of 4, if note the pacman will pass walls
private int totalFrame = 4; // Cantidad total de animation frames por direccion
private int currentArrowFrame = 0; // animation frame de arrow actual
private long frameTicker; // tiempo desde que el ultimo frame fue dibujado
//----------------------------------------------------------------------------------------------
//Constructors
public GameView(Context context) {
super(context);
this.constructorHelper(context);
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
this.constructorHelper(context);
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.constructorHelper(context);
}
private void constructorHelper(Context context) {
this.gestureDetector = new GestureDetector(this);
this.setFocusable(true);
this.holder = getHolder();
this.holder.addCallback(this);
this.frameTicker = (long) (1000.0f / totalFrame);
this.gameManager=new GameManager(this);
int screenWidth=getResources().getDisplayMetrics().widthPixels;
this.blockSize = ((((screenWidth/this.gameManager.getGameMap().getMapWidth())/movementFluencyLevel)*movementFluencyLevel)/4)*4;
this.holder.setFixedSize(blockSize*this.gameManager.getGameMap().getMapWidth(),blockSize*this.gameManager.getGameMap().getMapHeight());
this.gameManager.getGameMap().loadBonusBitmaps(this);
this.gameManager.setPacman(new Pacman("pacman","",this,this.movementFluencyLevel));
Ghost.loadCommonBitmaps(this);
}
//----------------------------------------------------------------------------------------------
//Getters and setters
public int getBlockSize() {
return blockSize;
}
public GameManager getGameManager() {
return gameManager;
}
public int getMovementFluencyLevel(){return movementFluencyLevel;}
//----------------------------------------------------------------------------------------------
private synchronized void initGhost(){
if(!GHOST_INICIALIZED){
GHOST_INICIALIZED=true;
this.gameManager.initGhosts(this);
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void run() {
long gameTime;
Canvas canvas;
while (!holder.getSurface().isValid()) {
}
this.initGhost();
this.setFocusable(true);
while (canDraw) {
gameTime=System.currentTimeMillis();
if(gameTime > frameTicker + (totalFrame * 15)){
canvas = holder.lockCanvas();
if(canvas!=null){
if(this.updateFrame(gameTime,canvas)){
try {
Thread.sleep(3000);
}catch (Exception e){}
}
holder.unlockCanvasAndPost(canvas);
if(this.gameManager.checkWinLevel()){
canDraw=false;
this.gameManager.cancelThreads();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
//animation
Log.i("Game","You win");
}else if(!this.gameManager.getPacman().hasLifes()){
//we lost
canDraw=false;
this.gameManager.cancelThreads();
//animation
Log.i("Game","You lose");
}
}
}
}
}
// Method to capture touchEvents
@Override
public boolean onTouchEvent(MotionEvent event) {
//To swipe
//https://www.youtube.com/watch?v=32rSs4tE-mc
this.gestureDetector.onTouchEvent(event);
super.onTouchEvent(event);
return true;
}
//Chequea si se deberia actualizar el frame actual basado en el
// tiempo que a transcurrido asi la animacion
//no se ve muy rapida y mala
@RequiresApi(api = Build.VERSION_CODES.N)
private boolean updateFrame(long gameTime, Canvas canvas) {
Pacman pacman;
Ghost[] ghosts;
boolean pacmanIsDeath;
pacman=this.gameManager.getPacman();
ghosts=this.gameManager.getGhosts();
// Si el tiempo suficiente a transcurrido, pasar al siguiente frame
frameTicker = gameTime;
canvas.drawColor(Color.BLACK);
this.gameManager.getGameMap().draw(canvas, Color.BLUE,this.blockSize,this.gameManager.getLevel());
this.gameManager.moveGhosts(canvas,this.blockSize);
pacmanIsDeath=pacman.move(this.gameManager,canvas);
if(!pacmanIsDeath){
// incrementar el frame
pacman.changeFrame();
for(int i=0; i<ghosts.length;i++){
ghosts[i].changeFrame();
}
currentArrowFrame++;
currentArrowFrame%=7;
}else{
pacman.setNextDirection(' ');
for(int i=0; i<ghosts.length;i++){
ghosts[i].respawn();
}
}
return pacmanIsDeath;
}
//----------------------------------------------------------------------------------------------
//Callback methods
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void surfaceCreated(SurfaceHolder holder) {
canDraw = true;
this.thread= new Thread(this);
this.thread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
//----------------------------------------------------------------------------------------------
public void resume() {
this.canDraw = true;
thread = new Thread(this);
thread.start();
}
public void pause() {
this.canDraw = false;
while (true) {
try {
thread.join();
return;
} catch (InterruptedException e) {
// retry
}
break;
}
this.thread=null;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent downEvent, MotionEvent moveEvent, float velocityX, float velocityY) {
//To swipe
//https://www.youtube.com/watch?v=32rSs4tE-mc
float diffX, diffY;
Pacman pacman;
Log.i("Fling", "detected");
diffX = moveEvent.getX() - downEvent.getX();
diffY = moveEvent.getY() - downEvent.getY();
pacman=this.gameManager.getPacman();
if (Math.abs(diffX) > Math.abs(diffY)) {
//right or left swipe
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY) {
if (diffX > 0) {
//right
pacman.setNextDirection('r');
} else {
//left
pacman.setNextDirection('l');
}
}
} else {
//up or down swipe
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY) {
if (diffY > 0) {
//down
pacman.setNextDirection('d');
} else {
//up
pacman.setNextDirection('u');
}
}
}
return true;
}
}
最后是 GameManager 代码
package Game;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import com.example.pacman.R;
import org.jetbrains.annotations.NotNull;
import Game.Behavior.ChaseBehavior.*;
import Game.Character_package.Ghost;
import Game.Character_package.Pacman;
import Game.GameCountDown.*;
public class GameManager {
private static final int TOTAL_LEVELS=256;
private GameMap gameMap;
private int level,bonusResetTime,score;
private CountDownScareGhosts scareCountDown;
private Pacman pacman;
private Ghost[] ghosts;
private boolean fruitHasBeenInTheLevel;
public GameManager(GameView gameView){
this.fruitHasBeenInTheLevel=false;
this.score=0;
this.gameMap=new GameMap();
this.gameMap.loadMap1();
this.level=1;
this.ghosts=new Ghost[4];
this.bonusResetTime = 5000;
this.scareCountDown=null;
}
public void addScore(int s){
this.score+=s;
}
public int getScore() {
return this.score;
}
public int getLevel() {
return this.level;
}
public GameMap getGameMap() {
return this.gameMap;
}
public Ghost[] getGhosts(){
return this.ghosts;
}
public Ghost getGhost(int i) {
return this.ghosts[i];
}
public Pacman getPacman(){
return this.pacman;
}
public void setPacman(Pacman pacman){
this.pacman=pacman;
}
public void eatPallet(int posXMap, int posYMap){
this.score+=10;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
}
public void eatBonus(int posXMap,int posYMap){
this.score+=500;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
}
public void eatSuperPallet(int posXMap,int posYMap){
this.score+=50;
Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
//Si hay un timer andando lo cancelo y ejecuto otro
if (this.scareCountDown != null){
this.scareCountDown.cancel();
}
this.scareCountDown = new CountDownScareGhosts(this.ghosts,this.gameMap.getMap());
this.scareCountDown.start();
}
public void tryCreateBonus(){
//only if pacman has eaten 20 pallets we should allow the fruit appear
if(!this.fruitHasBeenInTheLevel && this.gameMap.getEatenPallets()>=20){
//to not allow the fruit be again in the level
this.fruitHasBeenInTheLevel=true;
new CountdownBonusThread(this.gameMap,this.bonusResetTime).start();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void moveGhosts(Canvas canvas,int blocksize) {
for (int i = 0; i < ghosts.length; i++) {
ghosts[i].move(this.gameMap.getMap(),this.pacman);
ghosts[i].draw(canvas);
}
}
public synchronized void initGhosts(@NotNull GameView gv) {
int[][]spawnPositions,cornersPositions, notUpDownPositions,defaultTargets;
int movementeFluency;
defaultTargets=this.gameMap.getDefaultGhostTarget();
notUpDownPositions=this.gameMap.getNotUpDownDecisionPositions();
spawnPositions=this.gameMap.getGhostsSpawnPositions();
cornersPositions=this.gameMap.getGhostsScatterTarget();
movementeFluency=gv.getMovementFluencyLevel();
//start position
// 5 blinky spawn [13, 11]
// 6 pinky spawn [15,11]
// 7 inky spawn [13,16]
// 8 clyde spawn [15,16]
this.ghosts=new Ghost[4];
ghosts[0] = new Ghost("blinky",gv,spawnPositions[0], cornersPositions[0] ,new BehaviorChaseAgressive(notUpDownPositions,movementeFluency,defaultTargets[0]),movementeFluency,notUpDownPositions,'l',defaultTargets[0]);
ghosts[1] = new Ghost("pinky",gv,spawnPositions[1],cornersPositions[1],new BehaviorChaseAmbush(notUpDownPositions,movementeFluency,defaultTargets[1]),movementeFluency,notUpDownPositions,'r',defaultTargets[1]);
ghosts[2] = new Ghost("inky",gv,spawnPositions[2],cornersPositions[2],new BehaviorChasePatrol(notUpDownPositions,this.ghosts[0],movementeFluency,defaultTargets[0]),movementeFluency,notUpDownPositions,'l',defaultTargets[0]);
ghosts[3] = new Ghost("clyde",gv,spawnPositions[3],cornersPositions[3],new BehaviorChaseRandom(notUpDownPositions,cornersPositions[3],movementeFluency,defaultTargets[1]),movementeFluency,notUpDownPositions,'r',defaultTargets[1]);
try{
Thread.sleep(200);
}catch(Exception e){}
for (int i=0;i<ghosts.length;i++){
ghosts[i].onLevelStart(1);
}
}
public boolean checkWinLevel() {
//player win the level if he has eaten all the pallet
return this.gameMap.countPallets()==0;
}
public void onResume(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown.start();
}
}
public void onPause(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown=this.scareCountDown.onPause();
}
}
public void cancelThreads(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null){
this.scareCountDown.cancel();
}
}
}
目前我所知道的是我无法将 scoreTv 发送到 GameView,因为 GameView 由于不在同一个线程中而无法更改它。每当我之前告诉您的任何方法被调用时,我都需要 PlayActivity 实例做出反应。
好吧,我找到了一个解决方案,我不喜欢,艰难。
接下来我做了什么:
- 我在 AppCompatActivity 中创建了一个 STATIC SEMAPHORE
- 我也将它作为静态信号传递给 class GameManager,并且我已将此 class 中的分数更改为静态
- 最后,我在 PlayActivity 中 运行 一个线程,使用方法 运行OnUiThread 来更新 scoreTv,这个线程将获得一个许可,这个线程将在 SCORE 更新时被释放在 GameManager 中避免主动等待
您可能会问我为什么要标注 STATIC。如果我不这样做,我不知道为什么 GameManager 的引用会丢失。如果有人知道答案请post吧。
这里是 git 仓库的 link,这里是 classes
的代码
public class PlayActivity extends AppCompatActivity {
private TextView playerNickname;
private TextView scoreTv;
private TextView maxScore;
private SurfaceView gameSurfaceView;
private GameView gameView;
private GameManager gameManager;
private static Semaphore CHANGE_SCORE_MUTEX=new Semaphore(0,true);
private static Semaphore CHANGE_DIRECTION_MUTEX=new Semaphore(0,true);
private Thread changeScoreThread, changeDirectionThread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Modified code
setContentView(R.layout.activity_game);
//we get text view that we will use
playerNickname=(TextView) this.findViewById(R.id.tv_player);
scoreTv=(TextView) this.findViewById(R.id.tv_current_score);
maxScore=(TextView) this.findViewById(R.id.tv_current_max_score);
gameSurfaceView= (GameView) this.findViewById(R.id.game_view);
//set text view initial values
playerNickname.setText(getIntent().getExtras().getString("playerNickname"));
scoreTv.setText("0");
maxScore.setText("To modify");
this.gameView=new GameView(gameSurfaceView.getContext());
this.gameManager=this.gameView.getGameManager();
this.gameView.setSemaphores(CHANGE_SCORE_MUTEX,CHANGE_DIRECTION_MUTEX);
this.gameSurfaceView.getHolder().addCallback(this.gameView);
}
protected void onResume(){
super.onResume();
this.gameView.resume();
this.initChangerThreads();
}
public void updateScoreTv(int score){
this.scoreTv.setText(""+score);
}
protected void onPause(){
super.onPause();
this.gameView.pause();
//in order to stop the threads
CHANGE_SCORE_MUTEX.release();
CHANGE_DIRECTION_MUTEX.release();
}
public void onLose(double score){
//We try to save the score, if there is a previous register we write only if this score
//is better that the one before
DBManager manager;
long raw;
Score scoreToSave;
manager=new DBManager(this);
scoreToSave=new Score(this.playerNickname.toString(), score);
if(manager.saveScore(scoreToSave)==-1){
//if i couldn't save the score
if(manager.updateScore(scoreToSave)!=-1){
//if my new score is better than the one previous
}else{
//if my new score is worse or equal than the one previous
}
}
}
private void initChangerThreads() {
this.changeScoreThread = new Thread(new Runnable() {
public void run() {
while (gameView.isDrawing()) {
//Log.i("Score ",""+gameManager.getScore());
try {
CHANGE_SCORE_MUTEX.acquire();
runOnUiThread(new Runnable() {
@Override
public void run() {
updateScoreTv(gameView.getGameManager().getScore());
}
});
}catch (Exception e){}
}
}
});
this.changeScoreThread.start();
}
}
GameView:我刚刚添加了这个方法
public void setSemaphores(Semaphore changeScoreSemaphore, Semaphore changeDirectionSemaphore){
this.gameManager.setChangeScoreSemaphore(changeScoreSemaphore);
this.gameManager.getPacman().setChangeDirectionSemaphore(changeDirectionSemaphore);
Log.i("Semaphore", "setted");
}
游戏管理器
public class GameManager {
private static final int TOTAL_LEVELS=256;
private static int SCORE=0;
private GameMap gameMap;
private int level,bonusResetTime;//,score;
private CountDownScareGhosts scareCountDown;
private Pacman pacman;
private Ghost[] ghosts;
private boolean fruitHasBeenInTheLevel;
private static Semaphore CHANGE_SCORE_MUTEX;
public GameManager(){
this.fruitHasBeenInTheLevel=false;
//this.score=0;
this.gameMap=new GameMap();
this.gameMap.loadMap1();
this.level=1;
this.ghosts=new Ghost[4];
this.bonusResetTime = 5000;
this.scareCountDown=null;
}
public void setChangeScoreSemaphore(Semaphore changeScoreSemaphore) {
CHANGE_SCORE_MUTEX = changeScoreSemaphore;
//if(this.changeScoreSemaphore==null){
// Log.i("Change Score Semaphore","I'm null");
//}else{
// Log.i("Change Score Semaphore","I'm not null");
//}
}
public void addScore(int s){
//this.score+=s;
SCORE+=s;
CHANGE_SCORE_MUTEX.release();
/*if(this.changeScoreSemaphore==null){
Log.i("Change Score Semaphore","I'm null");
}else{
Log.i("Change Score Semaphore","I'm not null");
}*/
//this.changeScoreSemaphore.release();
}
public int getScore() {
return SCORE;
//return this.score;
}
public int getLevel() {
return this.level;
}
public GameMap getGameMap() {
return this.gameMap;
}
public Ghost[] getGhosts(){
return this.ghosts;
}
public Pacman getPacman(){
return this.pacman;
}
public void setPacman(Pacman pacman){
this.pacman=pacman;
}
public void eatPallet(int posXMap, int posYMap){
SCORE+=10;
CHANGE_SCORE_MUTEX.release();
//this.score+=10;
Log.i("Score GM", ""+SCORE);
//Log.i("Score GM", ""+this.score);
this.gameMap.getMap()[posYMap][posXMap]=0;
//this.changeScoreSemaphore.release();
//if(this.changeScoreSemaphore==null){
// Log.i("Change Score Semaphore","I'm null");
//}else{
// Log.i("Change Score Semaphore","I'm not null");
//}
}
public void eatBonus(int posXMap,int posYMap){
SCORE+=500;
CHANGE_SCORE_MUTEX.release();
//this.score+=500;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
//this.changeScoreSemaphore.release();
}
public void eatSuperPallet(int posXMap,int posYMap){
SCORE+=50;
CHANGE_SCORE_MUTEX.release();
//this.score+=50;
this.gameMap.getMap()[posYMap][posXMap]=0;
//Si hay un timer andando lo cancelo y ejecuto otro
if (this.scareCountDown != null){
this.scareCountDown.cancel();
}
this.scareCountDown = new CountDownScareGhosts(this.ghosts,this.gameMap.getMap());
this.scareCountDown.start();
//this.changeScoreSemaphore.release();
}
public void tryCreateBonus(){
//only if pacman has eaten 20 pallets we should allow the fruit appear
if(!this.fruitHasBeenInTheLevel && this.gameMap.getEatenPallets()>=20){
//to not allow the fruit be again in the level
this.fruitHasBeenInTheLevel=true;
new CountdownBonusThread(this.gameMap,this.bonusResetTime).start();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void moveGhosts(Canvas canvas,int blocksize) {
for (int i = 0; i < ghosts.length; i++) {
ghosts[i].move(this.gameMap.getMap(),this.pacman);
ghosts[i].draw(canvas);
}
}
public synchronized void initGhosts(int blocksize, Resources res, String packageName,int movementFluency) {
int[][]spawnPositions,cornersPositions, notUpDownPositions,defaultTargets;
defaultTargets=this.gameMap.getDefaultGhostTarget();
notUpDownPositions=this.gameMap.getNotUpDownDecisionPositions();
spawnPositions=this.gameMap.getGhostsSpawnPositions();
cornersPositions=this.gameMap.getGhostsScatterTarget();
//start position
// 5 blinky spawn [13, 11]
// 6 pinky spawn [15,11]
// 7 inky spawn [13,16]
// 8 clyde spawn [15,16]
this.ghosts=new Ghost[4];
ghosts[0] = new Ghost("blinky",spawnPositions[0], cornersPositions[0] ,new BehaviorChaseAgressive(notUpDownPositions,movementFluency,defaultTargets[0]),movementFluency,notUpDownPositions,'l',defaultTargets[0],blocksize,res,packageName);
ghosts[1] = new Ghost("pinky",spawnPositions[1],cornersPositions[1],new BehaviorChaseAmbush(notUpDownPositions,movementFluency,defaultTargets[1]),movementFluency,notUpDownPositions,'r',defaultTargets[1],blocksize,res,packageName);
ghosts[2] = new Ghost("inky",spawnPositions[2],cornersPositions[2],new BehaviorChasePatrol(notUpDownPositions,this.ghosts[0],movementFluency,defaultTargets[0]),movementFluency,notUpDownPositions,'l',defaultTargets[0],blocksize,res,packageName);
ghosts[3] = new Ghost("clyde",spawnPositions[3],cornersPositions[3],new BehaviorChaseRandom(notUpDownPositions,cornersPositions[3],movementFluency,defaultTargets[1]),movementFluency,notUpDownPositions,'r',defaultTargets[1],blocksize,res,packageName);
try{
Thread.sleep(200);
}catch(Exception e){}
for (int i=0;i<ghosts.length;i++){
ghosts[i].onLevelStart(1);
}
}
public boolean checkWinLevel() {
//player win the level if he has eaten all the pallet
return this.gameMap.countPallets()==0;
}
public void onResume(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown.start();
}
}
public void onPause(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown=this.scareCountDown.onPause();
}
}
public void cancelThreads(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null){
this.scareCountDown.cancel();
}
}
}
我在任何地方都找不到这样的解决方案,如果您目前遇到这样的问题,我希望我能为您节省大量研究时间:D
继续
我正在编写吃豆人程序,它几乎完成了,但是我在显示得分和吃豆人当前方向的视图与绘制游戏的表面视图之间的通信方面遇到了问题。
PlayActivity(这是一个 AppCompatActivity)有一个 GameView(这是一个绘制游戏的 SurfaceView)并且知道 GameManager(我制作的一个 class 基本上知道 pacman,地图,幽灵,以及一切)。 PlayActivity 还有一个名为 scoreTv 的文本视图。我不知道如何更新这个文本视图。
想法是每当 GameManager 的方法 addScore(int),eatPallet(int, int), eatBonus(int,int) 时,scoreTv 都会更改其文本值; eatSuperPallet(int,int) 被调用。
这里是PlayActivity代码
package Activities;
import android.os.Bundle;
import android.view.SurfaceView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.pacman.DBManager;
import Game.GameView;
import com.example.pacman.R;
import com.example.pacman.Score;
public class PlayActivity extends AppCompatActivity {
private TextView playerNickname;
TextView score;
private TextView maxScore;
private SurfaceView gameSurfaceView;
private GameView gameView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Modified code
setContentView(R.layout.activity_game);
//we get text view that we will use
playerNickname=(TextView) this.findViewById(R.id.tv_player);
score=(TextView) this.findViewById(R.id.tv_current_score);
maxScore=(TextView) this.findViewById(R.id.tv_current_max_score);
gameSurfaceView= (GameView) this.findViewById(R.id.game_view);
//set text view initial values
playerNickname.setText(getIntent().getExtras().getString("playerNickname"));
score.setText("0");
maxScore.setText("To modify");
this.gameView=new GameView(gameSurfaceView.getContext());
this.gameSurfaceView.getHolder().addCallback(this.gameView);
}
protected void onResume(){
super.onResume();
this.gameView.resume();
}
protected void onPause(){
super.onPause();
this.gameView.pause();
}
public void updateScore(int score){
this.score.setText(""+score);
}
public void onLose(double score){
//We try to save the score, if there is a previous register we write only if this score
//is better that the one before
DBManager manager;
long raw;
Score scoreToSave;
manager=new DBManager(this);
scoreToSave=new Score(this.playerNickname.toString(), score);
if(manager.saveScore(scoreToSave)==-1){
//if i couldn't save the score
if(manager.updateScore(scoreToSave)!=-1){
//if my new score is better than the one previous
}else{
//if my new score is worse or equal than the one previous
}
}
}
}
这是游戏视图代码
package Game;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import Activities.PlayActivity;
import Game.Character_package.Ghost;
import Game.Character_package.Pacman;
public class GameView extends SurfaceView implements Runnable, SurfaceHolder.Callback, GestureDetector.OnGestureListener {
private static final float SWIPE_THRESHOLD = 2;
private static final float SWIPE_VELOCITY = 2;
private boolean GHOST_INICIALIZED=false;
private GestureDetector gestureDetector;
private GameManager gameManager;
private Thread thread; //game thread
private SurfaceHolder holder;
private boolean canDraw = false;
private int blockSize; // Ancho de la pantalla, ancho del bloque
private static int movementFluencyLevel=8; //this movement should be a multiple of the blocksize and multiple of 4, if note the pacman will pass walls
private int totalFrame = 4; // Cantidad total de animation frames por direccion
private int currentArrowFrame = 0; // animation frame de arrow actual
private long frameTicker; // tiempo desde que el ultimo frame fue dibujado
//----------------------------------------------------------------------------------------------
//Constructors
public GameView(Context context) {
super(context);
this.constructorHelper(context);
}
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
this.constructorHelper(context);
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.constructorHelper(context);
}
private void constructorHelper(Context context) {
this.gestureDetector = new GestureDetector(this);
this.setFocusable(true);
this.holder = getHolder();
this.holder.addCallback(this);
this.frameTicker = (long) (1000.0f / totalFrame);
this.gameManager=new GameManager(this);
int screenWidth=getResources().getDisplayMetrics().widthPixels;
this.blockSize = ((((screenWidth/this.gameManager.getGameMap().getMapWidth())/movementFluencyLevel)*movementFluencyLevel)/4)*4;
this.holder.setFixedSize(blockSize*this.gameManager.getGameMap().getMapWidth(),blockSize*this.gameManager.getGameMap().getMapHeight());
this.gameManager.getGameMap().loadBonusBitmaps(this);
this.gameManager.setPacman(new Pacman("pacman","",this,this.movementFluencyLevel));
Ghost.loadCommonBitmaps(this);
}
//----------------------------------------------------------------------------------------------
//Getters and setters
public int getBlockSize() {
return blockSize;
}
public GameManager getGameManager() {
return gameManager;
}
public int getMovementFluencyLevel(){return movementFluencyLevel;}
//----------------------------------------------------------------------------------------------
private synchronized void initGhost(){
if(!GHOST_INICIALIZED){
GHOST_INICIALIZED=true;
this.gameManager.initGhosts(this);
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void run() {
long gameTime;
Canvas canvas;
while (!holder.getSurface().isValid()) {
}
this.initGhost();
this.setFocusable(true);
while (canDraw) {
gameTime=System.currentTimeMillis();
if(gameTime > frameTicker + (totalFrame * 15)){
canvas = holder.lockCanvas();
if(canvas!=null){
if(this.updateFrame(gameTime,canvas)){
try {
Thread.sleep(3000);
}catch (Exception e){}
}
holder.unlockCanvasAndPost(canvas);
if(this.gameManager.checkWinLevel()){
canDraw=false;
this.gameManager.cancelThreads();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {}
//animation
Log.i("Game","You win");
}else if(!this.gameManager.getPacman().hasLifes()){
//we lost
canDraw=false;
this.gameManager.cancelThreads();
//animation
Log.i("Game","You lose");
}
}
}
}
}
// Method to capture touchEvents
@Override
public boolean onTouchEvent(MotionEvent event) {
//To swipe
//https://www.youtube.com/watch?v=32rSs4tE-mc
this.gestureDetector.onTouchEvent(event);
super.onTouchEvent(event);
return true;
}
//Chequea si se deberia actualizar el frame actual basado en el
// tiempo que a transcurrido asi la animacion
//no se ve muy rapida y mala
@RequiresApi(api = Build.VERSION_CODES.N)
private boolean updateFrame(long gameTime, Canvas canvas) {
Pacman pacman;
Ghost[] ghosts;
boolean pacmanIsDeath;
pacman=this.gameManager.getPacman();
ghosts=this.gameManager.getGhosts();
// Si el tiempo suficiente a transcurrido, pasar al siguiente frame
frameTicker = gameTime;
canvas.drawColor(Color.BLACK);
this.gameManager.getGameMap().draw(canvas, Color.BLUE,this.blockSize,this.gameManager.getLevel());
this.gameManager.moveGhosts(canvas,this.blockSize);
pacmanIsDeath=pacman.move(this.gameManager,canvas);
if(!pacmanIsDeath){
// incrementar el frame
pacman.changeFrame();
for(int i=0; i<ghosts.length;i++){
ghosts[i].changeFrame();
}
currentArrowFrame++;
currentArrowFrame%=7;
}else{
pacman.setNextDirection(' ');
for(int i=0; i<ghosts.length;i++){
ghosts[i].respawn();
}
}
return pacmanIsDeath;
}
//----------------------------------------------------------------------------------------------
//Callback methods
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void surfaceCreated(SurfaceHolder holder) {
canDraw = true;
this.thread= new Thread(this);
this.thread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
//----------------------------------------------------------------------------------------------
public void resume() {
this.canDraw = true;
thread = new Thread(this);
thread.start();
}
public void pause() {
this.canDraw = false;
while (true) {
try {
thread.join();
return;
} catch (InterruptedException e) {
// retry
}
break;
}
this.thread=null;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent downEvent, MotionEvent moveEvent, float velocityX, float velocityY) {
//To swipe
//https://www.youtube.com/watch?v=32rSs4tE-mc
float diffX, diffY;
Pacman pacman;
Log.i("Fling", "detected");
diffX = moveEvent.getX() - downEvent.getX();
diffY = moveEvent.getY() - downEvent.getY();
pacman=this.gameManager.getPacman();
if (Math.abs(diffX) > Math.abs(diffY)) {
//right or left swipe
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY) {
if (diffX > 0) {
//right
pacman.setNextDirection('r');
} else {
//left
pacman.setNextDirection('l');
}
}
} else {
//up or down swipe
if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY) {
if (diffY > 0) {
//down
pacman.setNextDirection('d');
} else {
//up
pacman.setNextDirection('u');
}
}
}
return true;
}
}
最后是 GameManager 代码
package Game;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import com.example.pacman.R;
import org.jetbrains.annotations.NotNull;
import Game.Behavior.ChaseBehavior.*;
import Game.Character_package.Ghost;
import Game.Character_package.Pacman;
import Game.GameCountDown.*;
public class GameManager {
private static final int TOTAL_LEVELS=256;
private GameMap gameMap;
private int level,bonusResetTime,score;
private CountDownScareGhosts scareCountDown;
private Pacman pacman;
private Ghost[] ghosts;
private boolean fruitHasBeenInTheLevel;
public GameManager(GameView gameView){
this.fruitHasBeenInTheLevel=false;
this.score=0;
this.gameMap=new GameMap();
this.gameMap.loadMap1();
this.level=1;
this.ghosts=new Ghost[4];
this.bonusResetTime = 5000;
this.scareCountDown=null;
}
public void addScore(int s){
this.score+=s;
}
public int getScore() {
return this.score;
}
public int getLevel() {
return this.level;
}
public GameMap getGameMap() {
return this.gameMap;
}
public Ghost[] getGhosts(){
return this.ghosts;
}
public Ghost getGhost(int i) {
return this.ghosts[i];
}
public Pacman getPacman(){
return this.pacman;
}
public void setPacman(Pacman pacman){
this.pacman=pacman;
}
public void eatPallet(int posXMap, int posYMap){
this.score+=10;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
}
public void eatBonus(int posXMap,int posYMap){
this.score+=500;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
}
public void eatSuperPallet(int posXMap,int posYMap){
this.score+=50;
Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
//Si hay un timer andando lo cancelo y ejecuto otro
if (this.scareCountDown != null){
this.scareCountDown.cancel();
}
this.scareCountDown = new CountDownScareGhosts(this.ghosts,this.gameMap.getMap());
this.scareCountDown.start();
}
public void tryCreateBonus(){
//only if pacman has eaten 20 pallets we should allow the fruit appear
if(!this.fruitHasBeenInTheLevel && this.gameMap.getEatenPallets()>=20){
//to not allow the fruit be again in the level
this.fruitHasBeenInTheLevel=true;
new CountdownBonusThread(this.gameMap,this.bonusResetTime).start();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void moveGhosts(Canvas canvas,int blocksize) {
for (int i = 0; i < ghosts.length; i++) {
ghosts[i].move(this.gameMap.getMap(),this.pacman);
ghosts[i].draw(canvas);
}
}
public synchronized void initGhosts(@NotNull GameView gv) {
int[][]spawnPositions,cornersPositions, notUpDownPositions,defaultTargets;
int movementeFluency;
defaultTargets=this.gameMap.getDefaultGhostTarget();
notUpDownPositions=this.gameMap.getNotUpDownDecisionPositions();
spawnPositions=this.gameMap.getGhostsSpawnPositions();
cornersPositions=this.gameMap.getGhostsScatterTarget();
movementeFluency=gv.getMovementFluencyLevel();
//start position
// 5 blinky spawn [13, 11]
// 6 pinky spawn [15,11]
// 7 inky spawn [13,16]
// 8 clyde spawn [15,16]
this.ghosts=new Ghost[4];
ghosts[0] = new Ghost("blinky",gv,spawnPositions[0], cornersPositions[0] ,new BehaviorChaseAgressive(notUpDownPositions,movementeFluency,defaultTargets[0]),movementeFluency,notUpDownPositions,'l',defaultTargets[0]);
ghosts[1] = new Ghost("pinky",gv,spawnPositions[1],cornersPositions[1],new BehaviorChaseAmbush(notUpDownPositions,movementeFluency,defaultTargets[1]),movementeFluency,notUpDownPositions,'r',defaultTargets[1]);
ghosts[2] = new Ghost("inky",gv,spawnPositions[2],cornersPositions[2],new BehaviorChasePatrol(notUpDownPositions,this.ghosts[0],movementeFluency,defaultTargets[0]),movementeFluency,notUpDownPositions,'l',defaultTargets[0]);
ghosts[3] = new Ghost("clyde",gv,spawnPositions[3],cornersPositions[3],new BehaviorChaseRandom(notUpDownPositions,cornersPositions[3],movementeFluency,defaultTargets[1]),movementeFluency,notUpDownPositions,'r',defaultTargets[1]);
try{
Thread.sleep(200);
}catch(Exception e){}
for (int i=0;i<ghosts.length;i++){
ghosts[i].onLevelStart(1);
}
}
public boolean checkWinLevel() {
//player win the level if he has eaten all the pallet
return this.gameMap.countPallets()==0;
}
public void onResume(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown.start();
}
}
public void onPause(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown=this.scareCountDown.onPause();
}
}
public void cancelThreads(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null){
this.scareCountDown.cancel();
}
}
}
目前我所知道的是我无法将 scoreTv 发送到 GameView,因为 GameView 由于不在同一个线程中而无法更改它。每当我之前告诉您的任何方法被调用时,我都需要 PlayActivity 实例做出反应。
好吧,我找到了一个解决方案,我不喜欢,艰难。 接下来我做了什么:
- 我在 AppCompatActivity 中创建了一个 STATIC SEMAPHORE
- 我也将它作为静态信号传递给 class GameManager,并且我已将此 class 中的分数更改为静态
- 最后,我在 PlayActivity 中 运行 一个线程,使用方法 运行OnUiThread 来更新 scoreTv,这个线程将获得一个许可,这个线程将在 SCORE 更新时被释放在 GameManager 中避免主动等待
您可能会问我为什么要标注 STATIC。如果我不这样做,我不知道为什么 GameManager 的引用会丢失。如果有人知道答案请post吧。 这里是 git 仓库的 link,这里是 classes
的代码public class PlayActivity extends AppCompatActivity {
private TextView playerNickname;
private TextView scoreTv;
private TextView maxScore;
private SurfaceView gameSurfaceView;
private GameView gameView;
private GameManager gameManager;
private static Semaphore CHANGE_SCORE_MUTEX=new Semaphore(0,true);
private static Semaphore CHANGE_DIRECTION_MUTEX=new Semaphore(0,true);
private Thread changeScoreThread, changeDirectionThread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Modified code
setContentView(R.layout.activity_game);
//we get text view that we will use
playerNickname=(TextView) this.findViewById(R.id.tv_player);
scoreTv=(TextView) this.findViewById(R.id.tv_current_score);
maxScore=(TextView) this.findViewById(R.id.tv_current_max_score);
gameSurfaceView= (GameView) this.findViewById(R.id.game_view);
//set text view initial values
playerNickname.setText(getIntent().getExtras().getString("playerNickname"));
scoreTv.setText("0");
maxScore.setText("To modify");
this.gameView=new GameView(gameSurfaceView.getContext());
this.gameManager=this.gameView.getGameManager();
this.gameView.setSemaphores(CHANGE_SCORE_MUTEX,CHANGE_DIRECTION_MUTEX);
this.gameSurfaceView.getHolder().addCallback(this.gameView);
}
protected void onResume(){
super.onResume();
this.gameView.resume();
this.initChangerThreads();
}
public void updateScoreTv(int score){
this.scoreTv.setText(""+score);
}
protected void onPause(){
super.onPause();
this.gameView.pause();
//in order to stop the threads
CHANGE_SCORE_MUTEX.release();
CHANGE_DIRECTION_MUTEX.release();
}
public void onLose(double score){
//We try to save the score, if there is a previous register we write only if this score
//is better that the one before
DBManager manager;
long raw;
Score scoreToSave;
manager=new DBManager(this);
scoreToSave=new Score(this.playerNickname.toString(), score);
if(manager.saveScore(scoreToSave)==-1){
//if i couldn't save the score
if(manager.updateScore(scoreToSave)!=-1){
//if my new score is better than the one previous
}else{
//if my new score is worse or equal than the one previous
}
}
}
private void initChangerThreads() {
this.changeScoreThread = new Thread(new Runnable() {
public void run() {
while (gameView.isDrawing()) {
//Log.i("Score ",""+gameManager.getScore());
try {
CHANGE_SCORE_MUTEX.acquire();
runOnUiThread(new Runnable() {
@Override
public void run() {
updateScoreTv(gameView.getGameManager().getScore());
}
});
}catch (Exception e){}
}
}
});
this.changeScoreThread.start();
}
}
GameView:我刚刚添加了这个方法
public void setSemaphores(Semaphore changeScoreSemaphore, Semaphore changeDirectionSemaphore){
this.gameManager.setChangeScoreSemaphore(changeScoreSemaphore);
this.gameManager.getPacman().setChangeDirectionSemaphore(changeDirectionSemaphore);
Log.i("Semaphore", "setted");
}
游戏管理器
public class GameManager {
private static final int TOTAL_LEVELS=256;
private static int SCORE=0;
private GameMap gameMap;
private int level,bonusResetTime;//,score;
private CountDownScareGhosts scareCountDown;
private Pacman pacman;
private Ghost[] ghosts;
private boolean fruitHasBeenInTheLevel;
private static Semaphore CHANGE_SCORE_MUTEX;
public GameManager(){
this.fruitHasBeenInTheLevel=false;
//this.score=0;
this.gameMap=new GameMap();
this.gameMap.loadMap1();
this.level=1;
this.ghosts=new Ghost[4];
this.bonusResetTime = 5000;
this.scareCountDown=null;
}
public void setChangeScoreSemaphore(Semaphore changeScoreSemaphore) {
CHANGE_SCORE_MUTEX = changeScoreSemaphore;
//if(this.changeScoreSemaphore==null){
// Log.i("Change Score Semaphore","I'm null");
//}else{
// Log.i("Change Score Semaphore","I'm not null");
//}
}
public void addScore(int s){
//this.score+=s;
SCORE+=s;
CHANGE_SCORE_MUTEX.release();
/*if(this.changeScoreSemaphore==null){
Log.i("Change Score Semaphore","I'm null");
}else{
Log.i("Change Score Semaphore","I'm not null");
}*/
//this.changeScoreSemaphore.release();
}
public int getScore() {
return SCORE;
//return this.score;
}
public int getLevel() {
return this.level;
}
public GameMap getGameMap() {
return this.gameMap;
}
public Ghost[] getGhosts(){
return this.ghosts;
}
public Pacman getPacman(){
return this.pacman;
}
public void setPacman(Pacman pacman){
this.pacman=pacman;
}
public void eatPallet(int posXMap, int posYMap){
SCORE+=10;
CHANGE_SCORE_MUTEX.release();
//this.score+=10;
Log.i("Score GM", ""+SCORE);
//Log.i("Score GM", ""+this.score);
this.gameMap.getMap()[posYMap][posXMap]=0;
//this.changeScoreSemaphore.release();
//if(this.changeScoreSemaphore==null){
// Log.i("Change Score Semaphore","I'm null");
//}else{
// Log.i("Change Score Semaphore","I'm not null");
//}
}
public void eatBonus(int posXMap,int posYMap){
SCORE+=500;
CHANGE_SCORE_MUTEX.release();
//this.score+=500;
//Log.i("Score", Double.toString(this.score).substring(0,Double.toString(this.score).indexOf('.')));
this.gameMap.getMap()[posYMap][posXMap]=0;
//this.changeScoreSemaphore.release();
}
public void eatSuperPallet(int posXMap,int posYMap){
SCORE+=50;
CHANGE_SCORE_MUTEX.release();
//this.score+=50;
this.gameMap.getMap()[posYMap][posXMap]=0;
//Si hay un timer andando lo cancelo y ejecuto otro
if (this.scareCountDown != null){
this.scareCountDown.cancel();
}
this.scareCountDown = new CountDownScareGhosts(this.ghosts,this.gameMap.getMap());
this.scareCountDown.start();
//this.changeScoreSemaphore.release();
}
public void tryCreateBonus(){
//only if pacman has eaten 20 pallets we should allow the fruit appear
if(!this.fruitHasBeenInTheLevel && this.gameMap.getEatenPallets()>=20){
//to not allow the fruit be again in the level
this.fruitHasBeenInTheLevel=true;
new CountdownBonusThread(this.gameMap,this.bonusResetTime).start();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void moveGhosts(Canvas canvas,int blocksize) {
for (int i = 0; i < ghosts.length; i++) {
ghosts[i].move(this.gameMap.getMap(),this.pacman);
ghosts[i].draw(canvas);
}
}
public synchronized void initGhosts(int blocksize, Resources res, String packageName,int movementFluency) {
int[][]spawnPositions,cornersPositions, notUpDownPositions,defaultTargets;
defaultTargets=this.gameMap.getDefaultGhostTarget();
notUpDownPositions=this.gameMap.getNotUpDownDecisionPositions();
spawnPositions=this.gameMap.getGhostsSpawnPositions();
cornersPositions=this.gameMap.getGhostsScatterTarget();
//start position
// 5 blinky spawn [13, 11]
// 6 pinky spawn [15,11]
// 7 inky spawn [13,16]
// 8 clyde spawn [15,16]
this.ghosts=new Ghost[4];
ghosts[0] = new Ghost("blinky",spawnPositions[0], cornersPositions[0] ,new BehaviorChaseAgressive(notUpDownPositions,movementFluency,defaultTargets[0]),movementFluency,notUpDownPositions,'l',defaultTargets[0],blocksize,res,packageName);
ghosts[1] = new Ghost("pinky",spawnPositions[1],cornersPositions[1],new BehaviorChaseAmbush(notUpDownPositions,movementFluency,defaultTargets[1]),movementFluency,notUpDownPositions,'r',defaultTargets[1],blocksize,res,packageName);
ghosts[2] = new Ghost("inky",spawnPositions[2],cornersPositions[2],new BehaviorChasePatrol(notUpDownPositions,this.ghosts[0],movementFluency,defaultTargets[0]),movementFluency,notUpDownPositions,'l',defaultTargets[0],blocksize,res,packageName);
ghosts[3] = new Ghost("clyde",spawnPositions[3],cornersPositions[3],new BehaviorChaseRandom(notUpDownPositions,cornersPositions[3],movementFluency,defaultTargets[1]),movementFluency,notUpDownPositions,'r',defaultTargets[1],blocksize,res,packageName);
try{
Thread.sleep(200);
}catch(Exception e){}
for (int i=0;i<ghosts.length;i++){
ghosts[i].onLevelStart(1);
}
}
public boolean checkWinLevel() {
//player win the level if he has eaten all the pallet
return this.gameMap.countPallets()==0;
}
public void onResume(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown.start();
}
}
public void onPause(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null && !this.scareCountDown.hasEnded()){
this.scareCountDown=this.scareCountDown.onPause();
}
}
public void cancelThreads(){
for (int i=0 ; i<this.ghosts.length;i++){
this.ghosts[i].cancelBehavoirThread();
}
if(this.scareCountDown!=null){
this.scareCountDown.cancel();
}
}
}
我在任何地方都找不到这样的解决方案,如果您目前遇到这样的问题,我希望我能为您节省大量研究时间:D