最初我的游戏耗费大约500MB的疯狂内存,然后运行大约5倍的时间然后崩溃。
我知道这是内存泄漏但无法识别所以我决定在这里上传整个代码并寻求社区的帮助。
我没有使用任何框架,并从这个tutorial中学习,这是我的大学项目。但是我修改了大部分代码以适应我想要制作的游戏。
MyActivity.Java
public class MyActivity extends AppCompatActivity implements
View.OnClickListener {
private AnimationDrawable animationDrawable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
//To portarit
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.rlMy);
ImageButton buttonPlay = (ImageButton) findViewById(R.id.buttonPlay);
ImageButton buttonScore = (ImageButton) findViewById(R.id.buttonScore);
ImageButton buttonInstruction = (ImageButton) findViewById(R.id.buttonInstruction);
ImageButton buttonDeveloper = (ImageButton) findViewById(R.id.buttonDeveloper);
buttonPlay.setOnClickListener(this);
buttonScore.setOnClickListener(this);
buttonInstruction.setOnClickListener(this);
buttonDeveloper.setOnClickListener(this);
relativeLayout.setBackgroundResource(R.drawable.mainscreen);
animationDrawable = (AnimationDrawable) relativeLayout.getBackground();
animationDrawable.start();
}
private void clearData() {
try {
// clearing app data
Runtime runtime = Runtime.getRuntime();
runtime.exec("pm clear com.lud.root.jetfighter;");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.buttonPlay:
startActivity(new Intent(this, GameActivity.class));
finish();
break;
case R.id.buttonScore:
startActivity(new Intent(this, HighScore.class));
finish();
break;
case R.id.buttonDeveloper:
startActivity(new Intent(this, WelcomeActivity.class));
finish();
break;
case R.id.buttonInstruction:
startActivity(new Intent(this, Instruction.class));
finish();
break;
}
}
@Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are You Sure you want to exit ?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
GameView.stopMusic();
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startActivity(startMain);
finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
protected void onResume() {
super.onResume();
System.gc();
animationDrawable.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
this.releaseInstance();
unbindDrawables(findViewById(R.id.rlMy));
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
}
GameView.JAVA
public class GameView extends SurfaceView implements Runnable{
volatile boolean playing;
private Thread gameThread = null;
//Adding the player to this class
private Player player;
//Objects used for drawing
private Paint paint;
private Canvas canvas;
private SurfaceHolder surfaceHolder;
//Add stars list
private ArrayList<Star> stars = new ArrayList<Star>();
//Adding Enemies
private Enemy[] enemies; // only one enemy to decrease the difficulty
private int enemyCount = 3; // Number Of Enemies
private Boom boom;
int screenX;
private boolean isGameOver;
int score;
int highScore[] = new int[6];
float distance[] = new float[enemyCount];
public SharedPreferences sharedPreferences;
static MediaPlayer gameOnSound;
final MediaPlayer gameOverSound;
//Context to be used in onTouchEvent on GameOver Screen , for transition from
//GameOver Screen to Main Activity
Context context;
public GameView(Context context, int screenX, int screenY) {
super(context);
player = new Player(context, screenX, screenY);
this.context = context;
//initialize drawing objects
surfaceHolder = getHolder();
paint = new Paint();
int starNums = 50;
for (int i = 0; i < starNums; i++){
Star s = new Star(screenX,screenY);
stars.add(s);
}
enemies = new Enemy[enemyCount];
enemies[0] = new Enemy(context, screenX, screenY); // This needs to be created so that the next enemies
// created can be kept apart and not overlapping
for (int i=1; i<enemyCount; i++)
enemies[i] = new Enemy(context, screenX, screenY, enemies[i-1].getY(),enemies[i-1].getRadius());
boom = new Boom(context);
boom.setX(-250);
boom.setY(-250);
this.screenX = screenX;
isGameOver = false;
score = 0;
sharedPreferences = context.getSharedPreferences("Scores", Context.MODE_PRIVATE);
//initialize the array of high scores
highScore[0] = sharedPreferences.getInt("score1",0);
highScore[1] = sharedPreferences.getInt("score2",0);
highScore[2] = sharedPreferences.getInt("score3",0);
highScore[3] = sharedPreferences.getInt("score4",0);
highScore[4] = sharedPreferences.getInt("score5",0);
gameOnSound = MediaPlayer.create(context,R.raw.gameon);
gameOverSound = MediaPlayer.create(context,R.raw.gameover);
gameOnSound.setLooping(true);
gameOnSound.start();
}
@Override
public void run() {
while (playing){
update();
draw();
control();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_UP:
//stopping the booster when the screen is released
player.stopSlowing();
break;
case MotionEvent.ACTION_DOWN:
//starting the booster when the screen is released
player.startSlowing();
break;
}
if(isGameOver){
if(event.getAction() == MotionEvent.ACTION_DOWN)
context.startActivity(new Intent(context,MyActivity.class));
}
return true;
}
private void update() {
//score based on the time passed
score++;
player.update();
for(Star s : stars)
s.update();
// Below Code is for multiple enemy
for(int i=0; i < enemyCount; i++){
enemies[i].update((int) player.getSpeed() + 10 );
distance[i] = (float) Math.sqrt((player.getX()-enemies[i].getX())*(player.getX()-enemies[i].getX()) + (player.getY()-enemies[i].getY())*(player.getY()-enemies[i].getY()));
if(distance[i] < (30 + enemies[i].getRadius())){
boom.setX(player.getX());
boom.setY(player.getY());
player.setX(-200);
isGameOver = true;
playing = false;
gameOnSound.stop();
gameOverSound.start();
highScore[5] = score;
Arrays.sort(highScore);
Log.d("score","Score : "+score+"\nHighScores : "+highScore[0]+"\n"+highScore[1]+"\n"+highScore[2]+"\n"+highScore[3]+"\n"+highScore[4]+"\n"+highScore[5]);
SharedPreferences pref;
pref = context.getSharedPreferences("Scores", Context.MODE_PRIVATE);
SharedPreferences.Editor e = pref.edit();
for(int j=0;j<5;j++){
e.putInt("score"+(j+1),highScore[5-j]);
e.apply();
Log.d("score","score"+(j+1)+"\nHighScores : "+highScore[5-j]);
}
}
}
}
private void draw() {
if(surfaceHolder.getSurface().isValid()){
//lock the canvas
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.BLACK);
//setting the paint color to white to draw the stars
paint.setColor(Color.WHITE);
//drawing all the stars
for (Star s : stars){
paint.setStrokeWidth(s.getStarWidth());
canvas.drawPoint(s.getX(), s.getY(), paint);
}
paint.setTextSize(30);
canvas.drawText("Score : "+score,100,50,paint);
//The following line is for single enemy
paint.setColor(Color.CYAN);
for(int i = 0; i< enemyCount; i++)
canvas.drawCircle(enemies[i].getX(), enemies[i].getY(), enemies[i].getRadius(), paint);
canvas.drawBitmap(boom.getBitmap(),boom.getX(),boom.getY(),paint);
paint.setColor(Color.RED);
canvas.drawCircle(player.getX(), player.getY(), player.getRadius(), paint);
if(isGameOver){
paint.setTextSize(150);
paint.setTextAlign(Paint.Align.CENTER);
int yPos = (int)((canvas.getHeight() / 2) - ((paint.descent() + paint.ascent()))/2);
canvas.drawText("GAME OVER",canvas.getWidth()/2, yPos,paint);
}
//Unlocking the canvas
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
private void control() {
try {
gameThread.sleep(15); //creating the frame rate to around 33fps
canvas = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void pause(){
//pausing the game , set the variable to false
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resume(){
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
public static void stopMusic(){
gameOnSound.stop();
gameOnSound.release();
}
public static void pauseMusic(){
gameOnSound.pause();
}
public static void resumeMusic(){
gameOnSound.start();
}
}
Player.Java
public class Player {
private Bitmap bitmap;
//coordinates
private int x;
private int y;
private float speed ;
private boolean slowing, goingDown;
private int GRAVITY = -10; // For the gravity effect on the ship
//Boundaries
private int maxY;
private int minY;
private int screenY;
private float radius;
private final float MINRADIUS = 30;
private final float MAXRADIUS = 100;
private final int POSGRAVITY = +10;
private final int NEGGRAVITY = -10;
private final int SLOWPOSGRAVITY = +4;
private final int SLOWNEGGRAVITY = -4;
public Player(Context context, int screenX, int screenY){
x = 30; //initial x-position
y = 50; //initial y-position
speed = -4;
radius = MINRADIUS;
maxY = screenY - 30;
this.screenY = screenY;
minY = 30; //equal to initial radius
slowing = false; //initially slowing is false
goingDown = true;
}
public void startSlowing(){
slowing = true;
if(radius > MAXRADIUS)
radius = MAXRADIUS;
else radius += 1.5f;
if(goingDown)
{
speed = -0.3f;
GRAVITY = SLOWNEGGRAVITY;
}
else{
speed = 0.3f;
GRAVITY = SLOWPOSGRAVITY;
}
}
public void stopSlowing(){
slowing = false;
if(goingDown)
{
speed = -4.0f;
GRAVITY = NEGGRAVITY;
}
else{
speed = 4.0f;
GRAVITY = POSGRAVITY;
}
}
public void update(){
minY = (int) getRadius();
maxY = screenY - (int) getRadius();
if(!slowing)
if(radius < MINRADIUS)
radius = MINRADIUS;
else radius -= 0.5f;
if(slowing)
if(radius > MAXRADIUS)
radius = MAXRADIUS;
else radius += 0.5f;
// Moving the ball down
y -= speed + GRAVITY;
if(y < minY)
{
y = minY;
if(slowing)
{
speed = -0.3f;
GRAVITY = SLOWNEGGRAVITY;
}
else
{
speed = -4;
GRAVITY = NEGGRAVITY;
}
goingDown = true;
Log.d("down","Gravity : "+ GRAVITY + " Speed : "+speed+" y : "+y+" goingDown : "+goingDown);
}
if( y > maxY)
{
y = maxY;
if(slowing)
{
speed = 0.3f;
GRAVITY = SLOWPOSGRAVITY;
}
else
{
speed = 4;
GRAVITY = POSGRAVITY;
}
goingDown = false;
Log.d("down","Gravity : "+ GRAVITY + " Speed : "+speed+" y : "+y+" goingDown : "+goingDown);
}
}
public float getSpeed() {
return speed;
}
public int getY() {
return y;
}
public int getX() {
return (int) (getRadius());
}
public void setX(int x) {
this.x = x;
}
public float getRadius() {
return radius;
}
}
Enemy.Java
public class Enemy {
private int x;
private int y;
private int speed = -1;
private int radius = 20;
private int maxX, minX;
private int maxY, minY;
//Create a rect object to detect collision
private Rect detectCollision;
public Enemy(Context context, int screenX, int screenY){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
// Randomly generating enemy position
Random generator = new Random();
speed = 25;
radius = 35;
x = screenX;
y = generator.nextInt(maxY) + 10 - radius;
}
public Enemy(Context context, int screenX, int screenY, int prevY, int prevRad){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
// Randomly generating enemy position
Random generator = new Random();
speed = 25;
radius = 35;
x = screenX;
do{
y = generator.nextInt(maxY) + 10 - radius;
}while ((y - prevY) < (prevRad + radius + 60));
}
public void update(int playerSpeed){
// As the enemy moves from right to left
x -= playerSpeed;
x -= speed;
Random generator = new Random();
if(x < minX - this.getRadius()){
speed = generator.nextInt(10) + 10;
radius = generator.nextInt(20) + 30;
x = maxX;
y = generator.nextInt(maxY) - radius;
}
}
//This setter is used for changing the x coordinate after collision
public void setX(int x) {
this.x = x;
}
public Rect getDetectCollision() {
return detectCollision;
}
public int getSpeed() {
return speed;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
public int getRadius() {
return radius;
}
}
Star.Java
public class Star {
private int x;
private int y;
private int speed;
private int maxX;
private int maxY;
private int minX;
private int minY;
public Star(int screenX, int screenY){
maxX = screenX;
maxY = screenY;
minX = 0;
minY = 0;
Random generator = new Random();
speed = generator.nextInt(10);
//generate random coordinate but keeping them inside the screen
x = generator.nextInt(maxX);
y = generator.nextInt(maxY);
}
public void update(){
//To animate the stars on the left side
//Used here is the player's speed
x -= 10;
x -=speed;
if(x < 0){
//Again start the stars from the right edge
//Thus creating an infinite background effect
x = maxX;
Random generator = new Random();
y = generator.nextInt(maxY);
speed = generator.nextInt(15);
}
}
public float getStarWidth(){
//Randomising the star width , for aesthetics
float minX = 1.0f;
float maxX = 4.0f;
Random rand = new Random();
return rand.nextFloat() * (maxX - minX) + minX;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
}
所有这些类都使用 only canvas method 进行绘制,所以我想这里不应该有任何内存泄漏,但是当你输掉游戏时创建一个boom.png的以下Boom类确实使用了一个没有被缓存的位图,这可能是问题,但只有一个小位图不应该消耗这么疯狂的内存量,我确实通过GameView类中的control()函数改变帧速率但是仍然没有什么好处出来
Boom.Java
public class Boom {
private Bitmap bitmap;
private int x,y;
public Boom(Context context){
//get the image from drawable
bitmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.boom);
//set the coordinate outside the screen so that it won't be
//shown on the screen
//it will be visible for only a fraction of a second
x = -250;
y = -250;
}
public int getY() {
return y;
}
public int getX() {
return x;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap){
this.bitmap = bitmap;
}
public void setY(int y) {
this.y = y;
}
public void setX(int x) {
this.x = x;
}
}
我读过有关如何通过跟踪和分析来检查内存泄漏但是对于像我这样的新手来说太过于无法应对。
所有这些(DDMS,Android Monitor,Tracing,Profiling),但是我确实这样做了并且发现(基于Android Monitor中应用程序分析中的数据),我认为这是正确的,因为之前游戏正在运行只有3-4次,该工具显示随机功能过多。
之前在 Enemy.java 中有3次随机调用, Star.java中有1次调用
我想了解根本原因并删除阻碍我游戏的因素。
这是来自我的logcat :(我没有看到任何与清理有关的dalvikvm消息)
11-24 16:17:00.844 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.844 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.864 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.864 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.883 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.884 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.903 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.903 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.925 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.925 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.946 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.947 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.968 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.969 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:00.990 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:00.991 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.025 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.027 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.047 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.048 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.052 309 451 I BufferQueueProducer: [SurfaceView](this:0x7f8936e000,id:7877,api:2,p:2347,c:309) queueBuffer: fps=46.72 dur=1005.97 max=36.57 min=19.09
11-24 16:17:01.067 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.067 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.087 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.088 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.107 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.107 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.126 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.127 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.147 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.147 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.170 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.171 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.192 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.193 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.215 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.215 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.249 2347 2466 I SurfaceView: Locking canvas... stopped=false, win=android.view.SurfaceView$MyWindow@ab77912
11-24 16:17:01.250 2347 2466 I SurfaceView: Returned canvas: android.view.Surface$CompatibleCanvas@cee38e3
11-24 16:17:01.273 2347 2347 D MediaPlayer: handleMessage msg:(8, 0, 0)
11-24 16:17:01.276 2347 2466 D score : Score : 329
11-24 16:17:01.276 2347 2466 D score : HighScores : 248
11-24 16:17:01.276 2347 2466 D score : 269
11-24 16:17:01.276 2347 2466 D score : 317
11-24 16:17:01.276 2347 2466 D score : 329
11-24 16:17:01.276 2347 2466 D score : 460
11-24 16:17:01.276 2347 2466 D score : 579
11-24 16:17:01.282 2347 2466 D score : score1
此类承载GameView类。 的 GameActivity.clas
public class GameActivity extends AppCompatActivity{
private GameView gameView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Get the display object
Display display = getWindowManager().getDefaultDisplay();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//Get the screen Resolution
Point size = new Point();
display.getSize(size);
//initialize the gameview object
gameView = new GameView(this, size.x,size.y);
setContentView(gameView);
}
@Override
protected void onPause() {
super.onPause();
gameView.pause();
gameView.pauseMusic();
}
@Override
protected void onResume() {
super.onResume();
System.gc();
gameView.resume();
gameView.resumeMusic();
}
@Override
public void onBackPressed() {
startActivity(new Intent(this, MyActivity.class));
finish();
}
这是adb -d shell dumpsys meminfo com.lud.root.jetfighter
命令
游戏停止时的记忆输出
App Summary
Pss(KB)
------
Java Heap: 3348
Native Heap: 0
Code
:3320 筹码:332 图形:25175 私人其他:11696 系统:4691
TOTAL: 48562 TOTAL SWAP (KB): 30220
Objects
Views: 35 ViewRootImpl: 3
AppContexts: 5 Activities: 4
Assets: 4 AssetManagers: 2
Local Binders: 11 Proxy Binders: 18
Parcel memory: 3 Parcel count: 14
Death Recipients: 0 OpenSSL Sockets: 0
HPROC分析我的形象。我不知道该结论,但我想这是引起问题的图像按钮:
答案 0 :(得分:0)
我认为您在加载位图时阅读this会受益匪浅。根据我的个人经验,我发现如果没有正确完成,加载即使很小的位图也会导致内存大量消耗。
我注意到的一件事是,如果玩家被击中,你的绘制方法总是会引发boom.png。而不是设置x&amp; y值为负值然后在击中后使其成为正值,只需跟踪玩家是否被击中。您已经使用isGameOver执行此操作。只需移动那里的吊杆,除非有另一个需要绘制的实例(从我所知道的那里没有)。
除此之外,因为你说它在x次播放时崩溃了,我想知道它是否与你如何切换活动有关。假设您可以多次玩游戏并返回主菜单,我想知道您是否保留旧游戏视图的记录。你每次点击播放时都在创建一个新的GameView实例吗? (随意发布您的活动)