Why is game over screen lagging? - android

I have done a game in Android Studio with the goal of avoiding falling objects.
Generally the app is running good, but for some reason when i get to the game over screen and press an edittext to add highscore the game experience a lot of stutter and lag (with the keyboard und keypresses).
I have already called finish() (which can be seen in "FishView") on my main activity so I don't understand how it can be so slow on the game over screen, as it shouldn't have to worry about anything but the game over screen once it's there and the game over screen is very simple.
I'm having a hard time locating where the problem comes from, hence why I'm asking for help here.
Here is some code that I hope is sufficent for locating the problem:
MainActivity (deals with animation, level increase, spawn objects and interaction between objects and rules)
public class MainActivity extends AppCompatActivity implements GarbageListener {
//global variable of FishView
private FishView gameView;
//handle animation task
private final Handler handler = new Handler();
//global variable of screen
private RelativeLayout screen;
//time before level update
private int levelChangeTime = 3; //initialize small garbage in X seconds
private int spawnBossGarbage = 25; //initialize big garbage in X seconds
private int spawnHeart = 40; //initialize heart in X seconds
//pause variables
private Button pauseButton;
private boolean pauseFlag = false;
//left and right button
private Button leftButton;
private Button rightButton;
//List of small garbage on screen
private final List<SmallGarbage> smallGarbages = new ArrayList<>();
//List of big garbage on screen
private List<BigGarbage> bigGarbages = new ArrayList<>();
//List of heart on screen
private List<LifePoint> lifePoints = new ArrayList<>();
//create timer for animation and level increase
private Timer mainTimer;
//create timer fro holding left or right
private Timer movingLeft;
private Timer movingRight;
private final boolean buttonIsPressed = false; //so players can't hold both buttons down
private final int holdMovementPeriod = 9;
//keep track of song
public static Intent themeSong;
//keep track of how far we are in the song, serviceStop() deletes everything in service ThemeSong so variable must be saved elsewhere
public static int lengthOfSong = 0;
public static boolean backButtonPressed = false; //check if backButton was pressed in service ThemeSong oonDestroy() since that's the last thing that is run
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
themeSong=new Intent(this, ThemeSong.class);
startService(themeSong); //OR stopService(svc);
leftButton = findViewById(R.id.leftArrow);
rightButton = findViewById(R.id.rightArrow);
screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
screen.addView(gameView);
pauseButton = findViewById(R.id.pauseButton);
mainTimer = new Timer();
createNewAnimationTask();
createNewLevelTask();
//create listeners fo holding left or right button
findViewById(R.id.leftArrow).setOnTouchListener(new View.OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
holdLeft();
rightButton.setEnabled(false);}
if (event.getAction() == MotionEvent.ACTION_UP) {
rightButton.setEnabled(true);
if (movingLeft!=null){
movingLeft.cancel();
}}
return false;}
});
findViewById(R.id.rightArrow).setOnTouchListener(new View.OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
holdRight();
leftButton.setEnabled(false);}
if (event.getAction() == MotionEvent.ACTION_UP) {
leftButton.setEnabled(true);
if (movingRight!=null){
movingRight.cancel();}}
return false;}
});
}
public void moveLeft(#SuppressWarnings("unused") View v){
if (buttonIsPressed){return;}
gameView.setLeftPressed(true);
gameView.leftFishAnimation();//before running the animations we first set which fish animations to run (left or right)
gameView.invalidate();
}
public void moveRight(#SuppressWarnings("unused") View view) {
if (buttonIsPressed){return;}
gameView.setRightPressed(true);
gameView.rightFishAnimation();
gameView.invalidate();
}
public void pauseGame(View v){
String resume = "Resume";
String pause = "Pause";
if (!pauseFlag){
stopService(themeSong); //turn of music
pauseFlag = true;
pauseButton.setText(resume);
pauseButton.setBackgroundResource(R.drawable.roundbuttonred);
//disable animation and level tasks
mainTimer.cancel();
//disable all falling garbage on screen
for (SmallGarbage smallGarbage : smallGarbages) {smallGarbage.disableGarbageTimer();}
for (BigGarbage bigGarbage : bigGarbages) {bigGarbage.disableGarbageTimer();}
for (LifePoint lifePoint : lifePoints) {lifePoint.disableGarbageTimer();}
//disable buttons
leftButton.setEnabled(false);
rightButton.setEnabled(false);
}
else{
startService(themeSong); //start music
pauseFlag=false;
pauseButton.setText(pause);
leftButton.setEnabled(true);
rightButton.setEnabled(true);
pauseButton.setBackgroundResource(R.drawable.roundbuttonblue);
//resume falling garbage
for (SmallGarbage smallGarbage : smallGarbages) {smallGarbage.startFallingGarbage();}
for (BigGarbage bigGarbage : bigGarbages) {bigGarbage.startFallingGarbage();}
for (LifePoint lifePoint : lifePoints) {lifePoint.startFallingGarbage();}
//resume animation and level increase
mainTimer = new Timer();
createNewAnimationTask();
createNewLevelTask();
}
}
private void createNewAnimationTask(){
TimerTask newAnimationTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
//here we set the animation
int selectedFish = gameView.getSelectedFish();
selectedFish ++;
if (selectedFish==2){
selectedFish = 0;}
gameView.setSelectedFish(selectedFish);
//update screen
gameView.invalidate();
}
});
}
};
long animationPeriod = 600;
mainTimer.scheduleAtFixedRate(newAnimationTask, 0, animationPeriod);
}
private void createNewLevelTask(){
TimerTask levelCountDown = new TimerTask(){
#Override
public void run() {
levelChangeTime--;
spawnBossGarbage--;
spawnHeart--;
if (levelChangeTime==0 || spawnBossGarbage == 0 || spawnHeart == 0){
//move task that updates the UI onto the main thread
runOnUiThread(new Runnable() { //this tells the program to run this on the UI(aka main) thread, we could also call on new Thread if wanted to start new thread
#Override
public void run() {
if (levelChangeTime==0){generateNewGarbage("smallGarbage");}
if (spawnBossGarbage==0){generateNewGarbage("bigGarbage");}
if (spawnHeart==0){generateNewGarbage("lifePoint");}// when this is added we can't lose life?
}
});
}
}
};
mainTimer.scheduleAtFixedRate(levelCountDown,0,1000);
}
private void holdLeft(){
movingLeft = new Timer();
final View v = new View(this); //create view so moveLeft() can called
TimerTask holdLeftTask = new TimerTask(){
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
moveLeft(v);
}
});
}};
movingLeft.scheduleAtFixedRate(holdLeftTask,0,holdMovementPeriod);
}
private void holdRight(){
movingRight = new Timer();
final View v = new View(this);
TimerTask holdRightTask = new TimerTask(){
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
moveRight(v);
}
});
}};
movingRight.scheduleAtFixedRate(holdRightTask,0,holdMovementPeriod);
}
private void generateNewGarbage(String garbage){
switch (garbage){
case "bigGarbage":
spawnBossGarbage = 40; //time to next spawn
BigGarbage newBigGarbage = new BigGarbage(MainActivity.this);
newBigGarbage.setListener(MainActivity.this);
bigGarbages.add(newBigGarbage);
screen.addView(newBigGarbage);
break;
case "smallGarbage":
levelChangeTime = new Random().nextInt(20)+3; //set seconds between 3 and 20 at random
//this create SmallGarbage and initialize its task
SmallGarbage newGarbage = new SmallGarbage(MainActivity.this);
newGarbage.setListener(MainActivity.this); // set listener for garbage
smallGarbages.add(newGarbage);
screen.addView(newGarbage);
break;
case "lifePoint":
spawnHeart=30; //time to next spawn
//this create SmallGarbage and initialize its task
LifePoint newLifePoint = new LifePoint(MainActivity.this);
newLifePoint.setListener(MainActivity.this); // set listener for garbage
lifePoints.add(newLifePoint);
screen.addView(newLifePoint);
break;
}
}
//here starts the GarbageListener
#Override
public void handleAvoidedGarbage(String avoidedGarbage) {
gameView.avoidedGarbage(avoidedGarbage);
}
#Override
public boolean handleHitPlayer(int x, int y, String garbageType) {
return gameView.hitWasteChecker(x,y, garbageType);
}
#Override
public void handleLoseLife() {
gameView.loseLife();
}
//empty lives on screen, once they have landed or hit player
#Override
public void emptyLifePointList(){
lifePoints.clear();
lifePoints = new ArrayList<>();
}
//empty big garbage on screen, once they have landed or hit player
#Override
public void emptyBigGarbageList(){
bigGarbages.clear();
bigGarbages = new ArrayList<>();
}
//saving and setting length of played song
public static int getLengthOfSong() {
return lengthOfSong;
}
public static void setLengthOfSong(int lengthOfSong) {
MainActivity.lengthOfSong = lengthOfSong;
}
//onStop runs AFTER onBackPressed(), so lengthOfSong must be reset there
#Override
public void onBackPressed() {
super.onBackPressed();
backButtonPressed = true;
}
public static boolean isBackButtonPressed() {
return backButtonPressed;
}
public static void setBackButtonPressed(boolean backButtonPressed) {
MainActivity.backButtonPressed = backButtonPressed;
}
//this runs whenever the app is closed
#Override
protected void onStop(){
super.onStop();
//stop music
stopService(themeSong);
setLengthOfSong(0);
//pause game, this will also reset sound upon start
final View v = new View(this);
pauseFlag = false;
pauseGame(v);
}
}
FishView (deals with creating the player, rules and HANDELING the starting the game over screen)
public class FishView extends View {
private final Bitmap[] fish = new Bitmap[3];
private final Bitmap gameBackground;
private final Bitmap[] lifePoints = new Bitmap[2];
private int selectedFish;
private final Paint scorePaint = new Paint();
private int score, fishLives;
private static final int fishY = 1200;
private int fishX = 400;
private int speedX = 0;
private boolean leftPressed = false;
private boolean rightPressed = false;
public FishView(Context context) {
super(context);
//set background
gameBackground = BitmapFactory.decodeResource(getResources(),R.drawable.underwater);
//set default/start fish animations
leftFishAnimation();
//set selected fish animation to default start on 0
selectedFish = 0;
//set life points
lifePoints[1] = BitmapFactory.decodeResource(getResources(),R.drawable.lifepoint);
lifePoints[0] = BitmapFactory.decodeResource(getResources(),R.drawable.deadlife);
//set score
scorePaint.setColor(Color.WHITE);
scorePaint.setTextSize(80);
// scorePaint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); //??
scorePaint.setAntiAlias(true); //(graphic improvement) this removes the staircase effect aka make smoother
scorePaint.setTypeface(Typeface.SERIF);
score = 0;
//set fish lives
fishLives = 3;
}
//in a View, the onDraw method is called whenever:
//the view is initially drawn or whenever invalidate() is called on the view
//in our case we call on the constructor which initially the View
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//should maybe be canvas.getWidth() here
int canvasWidth=getWidth();
//set game boundaries
int minFishX = 0; //should not be able to go of screen (to the left)
int maxFishX = canvasWidth-fish[0].getWidth(); //furthers you can go to the right (to the right)
//check boundaries
if (fishX < minFishX) {
fishX = minFishX;
}
if (fishX > maxFishX) {
fishX = maxFishX;
}
//set position dependent on speed
fishX += speedX;
//draw background
canvas.drawBitmap(gameBackground, 0, 0, null);
//this draws the bitmap we decoded from the image
if (leftPressed){
speedX -= 15;
}
else if (rightPressed){
speedX += 15;
}
if (speedX != 0){
while (speedX != 0){
if (leftPressed){
fishX -= 1;
speedX += 1;
canvas.drawBitmap(fish[selectedFish],fishX,fishY,null);
invalidate();
}
else if (rightPressed){
fishX += 1;
speedX -= 1;
canvas.drawBitmap(fish[selectedFish],fishX,fishY,null);
invalidate();
}
}}
else{ //if nothing happens when we stay here
canvas.drawBitmap(fish[selectedFish],fishX,fishY, null);
}
leftPressed=false;
rightPressed=false;
//draw score
canvas.drawText("Score: " + score, 20 , 90, scorePaint);
//draw life points and life point we have lost
for (int lives = 0; lives < 3 ; lives++) {
int lifeX = 650 + 140*lives;
int lifeY = 10;
if (lives < fishLives){
canvas.drawBitmap(lifePoints[1],lifeX,lifeY,null);
}
else{
canvas.drawBitmap(lifePoints[0],lifeX,lifeY,null);
}
}
}
public boolean hitWasteChecker(int x, int y, String garbageType){
switch (garbageType){
//define hit boxes
//first check is how far above, second how much underneath, third how much to the left, and fourth how much to the right
case "smallGarbage":
return fishY <= y + 80 && fishY + fish[selectedFish].getHeight() >= y + 75 && fishX <= x + 75 && x + 20 <= (fishX + fish[selectedFish].getWidth());
case "bigGarbage":
return fishY <= y + 170 && fishY + fish[selectedFish].getHeight() >= y + 75 && fishX <= x + 180 && x + 20 <= (fishX + fish[selectedFish].getWidth());
case "lifePoint":
if (fishY <= y + 25 && fishY + fish[selectedFish].getHeight() >= y + 60 && fishX <= x + 110 && x + 35 <= (fishX + fish[selectedFish].getWidth())){
if (fishLives<3){fishLives++;
return true;} //if not full life gain a life
if (fishLives==3){score+=40; //if already full life then gain 40 points
return true;}}
return false;
default:
return false;
}}
public void loseLife(){
fishLives--;
if (fishLives<=0){
//stop theme song from playing
getContext().stopService(MainActivity.themeSong);
//through these lines a new Activity can be created from a View
Intent gameOverIntent = new Intent(getContext(), GameOverActivity.class);
gameOverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); //not possible to go back from game over screen
gameOverIntent.putExtra("final score", score); // send data to game over activity
getContext().startActivity(gameOverIntent);
((MainActivity) getContext()).overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
((MainActivity) getContext()).finish(); //TIMERS is till running
}
}
public void leftFishAnimation(){
fish[0] = BitmapFactory.decodeResource(getResources(),R.drawable.leftfish1);
fish[1] = BitmapFactory.decodeResource(getResources(),R.drawable.leftfish2);
}
public void rightFishAnimation(){
fish[0] = BitmapFactory.decodeResource(getResources(),R.drawable.rightfish1);
fish[1] = BitmapFactory.decodeResource(getResources(),R.drawable.rightfish2);
}
public void setLeftPressed(boolean leftPressed) {
this.leftPressed = leftPressed;
}
public void setRightPressed(boolean rightPressed) {
this.rightPressed = rightPressed;
}
public int getSelectedFish() {
return selectedFish;
}
public void setSelectedFish(int selectedFish) {
this.selectedFish = selectedFish;
}
public void avoidedGarbage(String avoidedGarbage){
switch (avoidedGarbage){
case "smallGarbage":
score += 10;
break;
case "bigGarbage":
score += 25;
break;
}
}
}
GameOver: (display the game over screen)
public class GameOverActivity extends AppCompatActivity {
//create instance of database
private DatabaseHelper db;
private EditText usernameInput;
private int score;
private MediaPlayer gameOverSound;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_over);
score = Objects.requireNonNull(getIntent().getExtras()).getInt("final score");
usernameInput = findViewById(R.id.addUsername);
db = new DatabaseHelper(this);
//easier way of doing it
gameOverSound = MediaPlayer.create(this, R.raw.gameoversound);
gameOverSound.setVolume(0.2f,0.2f);
gameOverSound.start();
String yourFinalScore = "Your final score: " + score;
TextView finalScore = findViewById(R.id.finalScore);
finalScore.setText(yourFinalScore);
}
public void restartGame(View v){
Intent restartIntent = new Intent(this, MainActivity.class);
restartIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); //so we can't go back to game over
startActivity(restartIntent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); //transition between activities
finish(); //end this activity, MainActivity is already ended so can't only call on finish here to go back
}
public void backToStartMenu(View view) {
Intent startMenuIntent = new Intent(this, MenuActivity.class);
startActivity(startMenuIntent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish(); //end this activity
}
public void addHighscore(View view) {
String writtenUsername = usernameInput.getText().toString();
if (!writtenUsername.equals("") && score != 0){
//insert writtenUsername and score into database
boolean insertedData = db.insertData(writtenUsername, score);
if (insertedData){
Toast.makeText(this, "Highscore was added", Toast.LENGTH_SHORT).show();
Intent startMenuIntent = new Intent(this, MenuActivity.class);
startActivity(startMenuIntent);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
finish();}
else{
Toast.makeText(this, "Highscore couldn't be added", Toast.LENGTH_SHORT).show();
}
}
}
//this runs whenever the app is closed, mobile arrow is pressed or we switch activity
#Override
protected void onStop(){
super.onStop();
gameOverSound.stop();
gameOverSound.release(); //solve error: if run twice the app will close because we cant release it twice
}
//if pressed mobile back button go back to start menu
#Override
public void onBackPressed() {
super.onBackPressed();
View v = new View(this);
backToStartMenu(v);
}
}
What I find weird about this problem is that the MainActivity, with the player animation and all the falling view objects, runs fine. However, something as small as the game over screen is lagging. This leaves me to believe that I somehow don't quit my Activities as I should which leads to the main thread not being able to handel it. Anyways thank you for your time! :)
Update:
I checked it and it seems to be some real problem with the transition from MainActivity to GameOver. Using Android help -> find-action -> profiler,
I was able to see that the memory useage was about 110 MB while in MainActivity and as soon as I got to the game over screen it went all the way up to 400 MB. But still I'm not able to locate why it occurs.

I figured it out. The problem was the sharp background image that I was using in my GameOver screen, apparently it was too much for the game too handle. So if you have any similar problem try changing the images and backgrounds on the screen.

Related

SetActive in Unity

I'm using Unity 2D for my game and I'm facing a weird problem. I have a GameObject (an image) in my scene with a button attached to it. When the player is out of the screen, the image that works as a GameOver panel appears and in case of enough coins, you can resume the game. Everything works fine but when the image appears and I click on button to resume, the image doesn't disappear while the function for decreasing number of coins still works.
Here is my script in which di() is for die and but() for button:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using CodeStage.AntiCheat.ObscuredTypes;
using CodeStage.AntiCheat.Detectors;
public class UIManager2 : MonoBehaviour {
public static ObscuredInt coin_score = 0;
public Text coin_text;
public Slider slider;
public Slider slider1;
public Slider slider2;
public GameObject bul;
public bool reverse;
public float timer;
public int delay = 1;
public float speed=0.5f;
public GameObject pause;
public AudioSource[] aud=new AudioSource[3];
void Start () {
coin_score = ObscuredPrefs.GetInt ("Score");
StartCoroutine (elapsed ());
slider1.minValue = 0;
slider1.maxValue = 20;
bul = GameObject.FindGameObjectWithTag ("Player").GetComponent<Plane19> ().bullet;
}
void Update () {
timer += Time.deltaTime;
if (timer >= delay && reverse == false) {
timer = 0f;
slider2.value++;
}
if (timer >= delay && reverse == true) {
timer = 0f;
slider2.value -= speed;
}
coin_text.text = coin_score.ToString ();
ObscuredPrefs.SetInt ("Score", coin_score);
if (slider2.value == 10) {
bul.SetActive (false);
reverse = true;
}
if (slider2.value == 0) {
bul.SetActive (true);
reverse = false;
}
}
public void di(){
pause.SetActive(true);
GetComponent<AudioSource>().Pause();
Time.timeScale = 0;
aud[0].Play();
aud[1].Pause();
aud[2].Pause();
}
public void but(){
pause.SetActive(false);
Time.timeScale=1;
aud[0].Pause();
aud[1].UnPause();
aud[2].UnPause();
GetComponent<AudioSource>().UnPause();
UIManager2.coin_score-=2;
}
IEnumerator elapsed () {
yield return new WaitForSeconds (2f);
slider.value++;
StartCoroutine (elapsed ());
}
}

FATAL EXCEPTION: Thread Android

I am trying to draw bullets on the screen when the player presses, but
after a few bullets I get the following error: FATAL EXCEPTION: Thread-2
Here is the classes of the bullet and of the gameView
public class Bullet {
private int x;
private int y;
private int speed;
private Bitmap bitmap;
public Bullet(Context context,int PositionX,int PositionY) {
Log.v("i am in bullet"," come on!!!");
speed = 10;
x = PositionX;
y = PositionY;
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.flash);
}
public void update() {
//Log.v("i am in update bullet"," come on!!!");
//animating the star horizontally right side
//by increasing x coordinate with player speed
x += 20;
}
public Bitmap getBitmap(){return bitmap;}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
returns
public class GameView extends SurfaceView implements Runnable {
private long fps;
private long timeThisFrame;
volatile boolean playing;
private Thread gameThread = null;
private Player player;
//a screenX holder
int screenX;
int i=0;
//context to be used in onTouchEvent to cause the activity transition from GameAvtivity to MainActivity.
Context context;
//the score holder
int score;
//the high Scores Holder
int highScore[] = new int[4];
//Shared Prefernces to store the High Scores
SharedPreferences sharedPreferences;
//to count the number of Misses
int countMisses;
//indicator that the enemy has just entered the game screen
boolean flag ;
//an indicator if the game is Over
private boolean isGameOver ;
private Paint paint;
private Canvas canvas;
private SurfaceHolder surfaceHolder;
private Enemy enemies;
//created a reference of the class Friend
private Friend friend;
//private Bullet bullet;
private ArrayList<Star> stars = new
ArrayList<Star>();
private ArrayList<Bullet> bullet = new
ArrayList<Bullet>();
//defining a boom object to display blast
private Boom boom;
//the mediaplayer objects to configure the background music
static MediaPlayer gameOnsound;
final MediaPlayer killedEnemysound;
final MediaPlayer gameOversound;
public GameView(Context context, int screenX, int screenY) {
super(context);
player = new Player(context, screenX, screenY);
surfaceHolder = getHolder();
paint = new Paint();
//initializing context
this.context = context;
int starNums = 20;
for (int i = 0; i < starNums; i++) {
Star s = new Star(context,screenX, screenY);
stars.add(s);
}
enemies = new Enemy(context,screenX,screenY);
//initializing boom object
boom = new Boom(context);
//initializing the Friend class object
friend = new Friend(context, screenX, screenY);
//setting the score to 0 initially
score = 0;
//setting the countMisses to 0 initially
countMisses = 0;
this.screenX = screenX;
isGameOver = false;
//initializing shared Preferences
sharedPreferences = context.getSharedPreferences("SHAR_PREF_NAME",Context.MODE_PRIVATE);
//initializing the array high scores with the previous values
highScore[0] = sharedPreferences.getInt("score1",0);
highScore[1] = sharedPreferences.getInt("score2",0);
highScore[2] = sharedPreferences.getInt("score3",0);
highScore[3] = sharedPreferences.getInt("score4",0);
//initializing the media players for the game sounds
gameOnsound = MediaPlayer.create(context,R.raw.gameon);
killedEnemysound = MediaPlayer.create(context,R.raw.killedenemy);
gameOversound = MediaPlayer.create(context,R.raw.gameover);
//starting the music to be played across the game
gameOnsound.start();
}
#Override
public void run() {
while (playing) {
long startFrameTime = System.currentTimeMillis();
synchronized (surfaceHolder) {
update();
draw();
control();
}
timeThisFrame = System.currentTimeMillis() - startFrameTime;
if (timeThisFrame >= 1) {
fps = 1000 / timeThisFrame;
}
}
}
private void update() {
//incrementing score as time passes
score++;
player.update();
//setting boom outside the screen
boom.setX(-250);
boom.setY(-250);
for (Star s : stars) {
s.update(player.getSpeed());
}
for (Bullet b : bullet) {
b.update();
}
//setting the flag true when the enemy just enters the screen
if(enemies.getX()==screenX){
flag = true;
}
enemies.update(player.getSpeed(),getFps());
}
private void draw() {
if (surfaceHolder.getSurface().isValid()) {
canvas = surfaceHolder.lockCanvas();
canvas.drawColor(Color.argb(500,135,206,250));
paint.setColor(Color.WHITE);
paint.setTextSize(20);
for (Star s : stars) {
canvas.drawBitmap(
s.getBitmap(),
s.getX(),
s.getY(),
paint);
}
for (Bullet b : bullet) {
canvas.drawBitmap(
b.getBitmap(),
b.getX(),
b.getY(),
paint);
}
canvas.drawBitmap(
player.getBitmap(),
player.getX(),
player.getY(),
paint);
canvas.drawBitmap( enemies.getBitmap(), enemies.getframeToDraw(), enemies.getwhereToDraw(), null);
//drawing the score on the game screen
paint.setTextSize(30);
canvas.drawText("Score:"+score,100,50,paint);
//drawing boom image
canvas.drawBitmap(
boom.getBitmap(),
boom.getX(),
boom.getY(),
paint
);
//draw game Over when the game is over
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);
}
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
private void control() {
try {
gameThread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void pause() {
playing = false;
try {
gameThread.join();
} catch (InterruptedException e) {
gameThread.interrupt();
}
}
public void resume() {
playing = true;
gameThread = new Thread(this);
gameThread.start();
}
//stop the music on exit
public static void stopMusic(){
gameOnsound.stop();
}
#Override
public boolean onTouchEvent(MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
player.stopBoosting();
break;
case MotionEvent.ACTION_DOWN:
player.setBoosting();
break;
}
//if touch the player
if (player.getDetectCollision().contains((int)motionEvent.getX(), (int)motionEvent.getY())) {
Bullet b = new Bullet(context,player.getX()+player.getDetectCollision().width()
,player.getY()-(player.getDetectCollision().height()/2));
bullet.add(b);
Log.d("test", "touch not inside myEditText");
}
//if the game's over, tappin on game Over screen sends you to MainActivity
if(isGameOver){
if(motionEvent.getAction()==MotionEvent.ACTION_DOWN){
context.startActivity(new Intent(context,MainActivity.class));
}
}
return true;
}
public long getFps(){
return fps;
}
}

Why the location of view always resets after I use ObjectAnimator to View.TRANSLATION_X?

I created a simple app, just want when I click a button, the imageview will travel to another position.
Below is the code, but it seems that when I click the button, the imageview always begin with the original position, not the real position. Is there any problem with my code?
public class MainActivity extends Activity{
private ImageView iv;
private Button bt;
private AnimatorSet mAniSet;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ddd);
iv = (ImageView) this.findViewById(R.id.imageView1);
bt = (Button) this.findViewById(R.id.button1);
mAniSet = new AnimatorSet();
bt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
int pointOnScreen[] = new int[2];
iv.getLocationOnScreen(pointOnScreen);
ObjectAnimator AniTranslationX = ObjectAnimator.ofFloat(iv, View.TRANSLATION_X, pointOnScreen[0]+100);
mAniSet.playTogether(AniTranslationX);
mAniSet.setTarget(iv);
mAniSet.setDuration(200).start();
}
});
}
}
If you create a ObjectAnimator.ofFloat(Object, Property, values) and there is only one value, so the origin of the animation will be 0f.
You can find the source into the class android.animation.KeyFrameSet and the following method:
public static KeyframeSet ofFloat(float... values) {
boolean badValue = false;
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
if (Float.isNaN(values[0])) {
badValue = true;
}
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] =
(FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
if (Float.isNaN(values[i])) {
badValue = true;
}
}
}
if (badValue) {
Log.w("Animator", "Bad value (NaN) in float animator");
}
return new FloatKeyframeSet(keyframes);
}
You can see, with only one value, the start of the animation is 0f (keyframes[0]).
If you put 2 values, your animation will start from the first value and stop at the second:
ObjectAnimator AniTranslationX = ObjectAnimator.ofFloat(iv, View.TRANSLATION_X, pointOnScreen[0], pointOnScreen[0] + 100);
With 3 or more values, the animation will reach all these values over the time of the animation.

Displaying a sequence of images with a time delay

I wanted to display 9 images one after the other. I have included the 9 images as an array:
imageHolders = new ArrayList<ImageView>();
imageHolders.add((ImageView) view.findViewById(R.id.imgOne));
imageHolders.add((ImageView) view.findViewById(R.id.imgTwo));
imageHolders.add((ImageView) view.findViewById(R.id.imgThree));
imageHolders.add((ImageView) view.findViewById(R.id.imgFour));
imageHolders.add((ImageView) view.findViewById(R.id.imgFive));
imageHolders.add((ImageView) view.findViewById(R.id.imgSix));
imageHolders.add((ImageView) view.findViewById(R.id.imgSeven));
imageHolders.add((ImageView) view.findViewById(R.id.imgEight));
imageHolders.add((ImageView) view.findViewById(R.id.imgNine));
This is what I have tried:
public void handleMessage(Message msg) {
int currentImage = 0;
int nextImage = 0;
// Logic to change the images
for (final ImageView imageView : imageHolders) {
currentImage = Integer.parseInt(imageView.getTag().toString());
if (currentImage > 1) {
nextImage = currentImage - 1;
} else {
nextImage = 9;
}
imageView.setTag(""+nextImage);
new CountDownTimer(10000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
imageView.setVisibility(VISIBLE);
}
}.start();
}
super.handleMessage(msg);
}
}
There is a delay between the first and the second images. I am not able to introduce a delay between the rest. I have no clue about introducing the delay. Any suggestions would be appreciated.
you could just use AnimationDrawable which does this for you , and allows you to set a duration of time (in milliseconds) using android:duration attribute .
The AnimationDrawable is probably the best answer, but for another solution you can look at AsyncTask.
http://developer.android.com/reference/android/os/AsyncTask.html
Then, you can in the background just sleep for some set amount of time, then display the next image.
You may want an event though to handle when this task is done, and in the listener you call the next one.
If you want to make just a simple static animation (all images at the same position) you can use AnimationDrawable but you can't easly change it dynamically - like change speed or something.
I just wrote a simple class for this so I can set multiple animations for one ImageView and change speed.
public class ImageSequence extends ImageView {
ArrayList<Drawable> draws = new ArrayList<Drawable>();
ArrayList<Integer> bnds = new ArrayList<Integer>(); //bounds of sequences - index of first and last frame.
ArrayList<String> names = new ArrayList<String>(); //sequences names.
Timer timer = new Timer(true);
TimerTask task;
int p = -1; //current playback position.
int cS = -1; //current sequence.
public ImageSequence(Context context) {
super(context);
createTask();
}
public void addSequence(String name, int[] res){
names.add(name);
bnds.add(draws.size());
bnds.add(draws.size() + res.length - 1);
for (int i = 0; i < res.length; i++){
draws.add(getContext().getResources().getDrawable(res[i]));
}
}
public void play(String sequence, float speed){
cS = -1;
for(int i = 0; i < names.size(); i++){
if(names.get(i) == sequence){ cS = i; break;}
}
if(cS<0) return;
p = -1;
task.run();
task.cancel();
if(speed <= 0) return;
createTask();
timer.schedule(task, (long) (1000/speed), (long) (1000/speed));
}
public void stop(){
task.cancel();
}
public void cancelTimer(){
timer.cancel();
}
private void createTask() {
task = new TimerTask(){
public void run() {
p++;
mHandler.obtainMessage(1).sendToTarget();
}
};
}
public Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
int l = p;
if(l > bnds.get(cS*2+1) || l < bnds.get(cS*2)) l = bnds.get(cS*2);
p = l;
ImageSequence.this.setImageDrawable(draws.get(l));
}
};
}
From outside you can use it like this:
seq.addSequence("orange", new int[]{R.drawable.ic_oval_diode_orange, R.drawable.ic_oval_diode_off});
seq.addSequence("green", new int[]{R.drawable.ic_oval_diode_green, R.drawable.ic_oval_diode_off});
seq.addSequence("red", new int[]{R.drawable.ic_oval_diode_red, R.drawable.ic_oval_diode_off});
seq.play("red", 4);
I'm not really experienced in Android and Java but it works and it do the job for me. Hope it will help someone.

Android Gallery freezes screen until scrolled

I have a full-screen custom Gallery. Elements of the gallery have buttons and other interactive areas. Some user interface is done through dialog boxes that pop in front of the gallery; when a dialog box is dismissed, the user is taken back to the gallery.
Most of the time, this works fine. However, sometimes, after a dialog box is dismissed, the buttons stop taking user input. The gallery, on the other hand, still scrolls. What's even more bizarre, as soon as I scroll the gallery, the system processes those clicks I thought failed (pops up a dialog, etc.).
It is easy to say that the main UI thread is locked. Why is it locked? How do I unlock it? Any help will be appreciated. Below is the full code of the class.
UPDATE. One of the elements within the Gallery is a HorizontalScrollView. When I try scroll it, mouse messages come through; I stepped through them and saw that the scrollBy() and invalidate() are properly called. Then I printed out the message queue. The only event that passes through the main loop is 1006, which I assume is the Touch event. The Draw event, the 1000, never makes it. Once I scroll the Gallery back and forth -- lo and behold -- the message queue starts receiving the 1000's, so the HorizontalScrollView scrolls fine!
So the question becomes: what stops the Draw events, and how do I make sure they are sent to the queue?
public class PlayerGallery extends Gallery
{
// 4 buttons to display
private final static int BUT_BIRD = 0;
private final static int BUT_SCORE = 1;
private final static int BUT_ROUND = 2;
private final static int BUT_MOVE = 3;
private final static int N_BUTTONS = 4;
// button images
private final static Drawable[] imgButtons =
{
WonDraw.WW.bird,
WonDraw.WW.scores,
WonDraw.WW.moveSumm,
WonDraw.WW.lastMove,
};
// individual player views
private PlayerView[] views;
// card that was clicked in the current player view
private Card clickedCard = null;
// wonder that was clicked in the current player view
private Player clickedWonderPlayer = null;
// one player
private final class PlayerView extends RelativeLayout
implements OnClickListener
{
// base view ID for the buttons
private final static int BUT_VIEW_ID = 200;
// player to display
final Player player;
// drawing data
private PlayerDisplay pd = null;
// the sub-view on top that shows player's hand
private HandView handView = null;
// button that takes user into bird's view
private final GlassButton[] buttons = new GlassButton[N_BUTTONS];
public PlayerView(Player player)
{
super(WonActivity.W.getApplicationContext());
setBackgroundColor(Color.TRANSPARENT);
this.player = player;
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_TOP);
handView = new HandView();
addView(handView, lp);
for (int i = 0; i < buttons.length; ++i)
{
lp = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (i % 2 == 0)
lp.addRule(ALIGN_PARENT_RIGHT);
else
lp.addRule(LEFT_OF, buttons[i - 1].getId());
lp.addRule(BELOW, i / 2 == 0 ? handView.getId() : buttons[i - 2].getId());
GlassButton but = new ImgGlassButton(GlassButton.ROUND, imgButtons[i]);
but.setId(BUT_VIEW_ID + i);
but.setOnClickListener(this);
buttons[i] = but; addView(but, lp);
}
}
// reset for the next player
void reset(boolean useBigCards)
{
pd = WonActivity.W.getDisplay(player.id, useBigCards);
if (useBigCards)
{
handView.pd = pd;
handView.hand = WonActivity.W.getCurrentHand();
handView.setVisibility(VISIBLE);
handView.requestLayout();
handView.scrollTo(0, 0);
}
else
{
handView.pd = null;
handView.hand = null;
handView.setVisibility(GONE);
}
for (int i = BUT_ROUND; i <= BUT_MOVE; ++i)
buttons[i].setEnabled(Table.T.movesAvailable());
invalidate();
}
#Override
protected void dispatchSetPressed(boolean pressed)
{
// if I don't do that, bird button gets pressed when I scroll the gallery!
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
int x = (int)event.getX();
int y = (int)event.getY();
clickedCard = pd.findSmallCard(x, y);
clickedWonderPlayer = clickedCard == null && pd.isInWonder(x, y) ?
player : null;
}
return super.onTouchEvent(event);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(MeasureSpec.makeMeasureSpec(WonActivity.W.getWidth() - 2, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(WonActivity.W.getHeight(), MeasureSpec.EXACTLY));
setBackgroundColor(Color.TRANSPARENT);
setBackgroundDrawable(WonDraw.W.getWood());
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (pd == null) return;
canvas.save();
pd.draw(canvas, handView.hand, true);
canvas.restore();
}
public void onClick(View v)
{
switch (v.getId() - BUT_VIEW_ID)
{
case BUT_BIRD:
WonActivity.W.switchToBird(player.id);
break;
case BUT_SCORE:
WonActivity.W.showScoreDlg();
break;
case BUT_ROUND:
WonActivity.W.showRoundDlg();
break;
case BUT_MOVE:
break;
}
}
};
// custom adapter for the gallery: provides circular functionality
private class PGAdapter extends BaseAdapter
{
public int getCount()
{
return Integer.MAX_VALUE;
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position)
{
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
return views[position % views.length];
}
};
public PlayerGallery()
{
super(WonActivity.W.getApplicationContext());
setSpacing(0);
setBackgroundColor(Color.TRANSPARENT);
views = new PlayerView[Table.T.getPlayerCount()];
for (int i = 0; i < Table.T.getPlayerCount(); ++i)
views[i] = new PlayerView(Table.T.getPlayer(i));
setAdapter(new PGAdapter());
setHorizontalFadingEdgeEnabled(false);
setVerticalFadingEdgeEnabled(false);
}
// reset for the next player
void changeMovingPlayer()
{
for (int i = 0; i < views.length; ++i)
views[i].reset(i == WonActivity.W.getCurrentPlayerID());
setViewedPlayer(Math.max(WonActivity.W.getCurrentPlayerID(), 0));
}
// set the player whose buildings to view
void setViewedPlayer(int index)
{
int pos = Integer.MAX_VALUE / 2;
pos -= pos % views.length;
setSelection(pos + index);
views[index].requestFocus();
views[index].invalidate();
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
int kEvent = e2.getX() > e1.getX() ?
KeyEvent.KEYCODE_DPAD_LEFT :
KeyEvent.KEYCODE_DPAD_RIGHT;
onKeyDown(kEvent, null);
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
boolean b = super.onTouchEvent(event);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
if (event.getEventTime() - event.getDownTime() < WonActivity.CLICK_MS)
{
if (clickedCard != null)
WonActivity.W.showCardDlg(clickedCard);
else if (clickedWonderPlayer != null)
WonActivity.W.showWonderDlg(clickedWonderPlayer);
}
clickedCard = null;
clickedWonderPlayer = null;
break;
}
return b;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);
WonActivity.W.resize(w, h);
super.onMeasure(MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY));
}
}

Categories

Resources