I am working on a game and have run into some issues. My architecture is something like this:
class GameView is used to draw bitmaps on my surfaces
class GameLoopThread is used to implement my game loop (if it wasn't obvious...)
class MovementUtils is used to hold all of my utilities related to moving objects
I want to house methods like gravity and movement controls in MovementUtils, but I'm having trouble actually updating the values in GameView. I tried using an intent, to no avail. I'll post my code, and maybe someone can show me what I should do. Also, ignore the Accelerometer lines, that's another problem entirely...
GameView.java
package com.example.connorgame;
import java.util.EventObject;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
public class GameView extends SurfaceView {
private Bitmap platform;
private Bitmap character;
private Bitmap background;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private MainActivity mainactivity;
private MovementUtils moveutil;
public float charX = 0;
public float charY = 0;
private boolean isFalling = true;
private boolean isJumping = false;
private float initialJumpY = 0;
private int initialFrame;
private int currentFrame;
private int frameDifference;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
mainactivity = (MainActivity) context;
moveutil = new MovementUtils();
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
});
platform = BitmapFactory.decodeResource(getResources(), R.drawable.platform);
character = BitmapFactory.decodeResource(getResources(), R.drawable.character);
background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(background, 0, 0, null);
canvas.drawBitmap(platform, 30, 700, null);
canvas.drawBitmap(character, charX, charY, null);
moveutil.gravity(charY, isFalling);
if (charY > getHeight() - character.getHeight()) {
initialFrame = gameLoopThread.numFrames;
initialJumpY = charY;
isFalling = false;
isJumping = true;
}
if (isJumping == true && isFalling == false) {
currentFrame = gameLoopThread.numFrames;
frameDifference = (currentFrame - initialFrame);
charY = charY - 5;
if (charY == initialJumpY - 100) {
isJumping = false;
isFalling = true;
}
}
}
}
MovementUtils.java
package com.example.connorgame;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
public class MovementUtils {
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
}
}
}
If I understand what you're doing correctly, you're trying to have charY in the GameView class modified by the gravity function. The issue is that float is a primitive, so it is based by value and only the local copy will be modified. If you passed an object instead, the object would be modified.
One solution is probably to just return the new Y position instead of trying to make gravity() change the Y position.
Another solution is to pass the GameView to MovementUtils and let MovementUtils modify it.
In other words, you would pass the GameView like this new MovementUtils(this);
And the gravity function would call a void setCharY(int y); in GameView.
public class MovementUtils {
private GameView gameView;
public MovementUtils(GameView view) {
this.gameView = view;
}
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
gameView.setCharY(charY);
}
}
}
Java only supports passing by value
So when you call moveutil.gravity(charY, isFalling);
and change it within the function, e.g.
public void gravity (float charY, boolean isFalling) {
if(isFalling == true){
charY = charY + 5;
}
Such change only occur in the local copy of the variable charY and does not affect the instance variable charY defined in your GameView class
To solve this, you can define your variables as objects as well as the argument to your functions, e.g.
public void gravity (Float charY, boolean isFalling) {}
And in your GameView class:
private Float charX = 0; // why you define them public !?
private Float charY = 0;
Alternatively, you can pass an instance of your GameView class or create a ValueObject class (similar to Context object in android) and use it to pass arguments
Related
So I have followed this tutorial and the code works perfectly. However I have some trouble understanding how OnTouchListener and OnTouch work together. I have spent a long time trauling this forum, websites and documentation to understand but still, I do not.
In this code, a OnTouchListener is set for ourSurfaceView, and then the onTouch is called for the activity?!
Can someone please explain the relationship of OnTouchListener and OnTouch across different activities and views. Many Thanks!
package com.games.michael.waterproofme;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
public class MainActivity extends Activity implements OnTouchListener{
MySurface ourSurfaceView;
float x,y,sX, sY, fX, fY;
Bitmap test, plus;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ourSurfaceView = new MySurface(this);
ourSurfaceView.setOnTouchListener(this);
x = 0;
y = 0;
sX = 0;
sY = 0;
fX = 0;
fY = 0;
test = BitmapFactory.decodeResource(getResources(), R.drawable.sportsball);//draw ball
plus = BitmapFactory.decodeResource(getResources(), R.drawable.plus);
setContentView(ourSurfaceView);
}
#Override
protected void onPause() {
super.onPause();
ourSurfaceView.pause();
}
#Override
protected void onResume() {
super.onResume();
ourSurfaceView.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
x = event.getX();
y = event.getY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
sX = event.getX();
sY = event.getY();
break;
case MotionEvent.ACTION_UP:
fX = event.getX();
fY = event.getY();
break;
}
return true;//false = finished dont loop through. true = loop through
}
public class MySurface extends SurfaceView implements Runnable{
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MySurface(Context context){
super(context);
ourHolder = getHolder();
ourThread = new Thread(this);
ourThread.start();
}
public void pause(){
isRunning = false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume(){
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
public void run() {
while(isRunning){
if(!ourHolder.getSurface().isValid()) {/
continue;}
Canvas canvas = ourHolder.lockCanvas();
canvas.drawRGB(02, 02, 150);
if (x != 0 && y != 0){
canvas.drawBitmap(test, x-(test.getWidth()/2), y-(test.getHeight()/2), null);//bitmap, left, top, paint
}
if (sX != 0 && sY != 0){
canvas.drawBitmap(plus, sX-(plus.getWidth()/2), sY-(plus.getHeight()/2), null);//bitmap, left, top, paint
}
if (fX != 0 && fY != 0){
canvas.drawBitmap(plus, fX-(plus.getWidth()/2), fY-(plus.getHeight()/2), null);//bitmap, left, top, paint
}
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
}
OnTouchListener is interface - class that implements it must override its methods. For android.view.View.OnTouchListener this is one method: boolean onTouch(View v, MotionEvent event)
When touch event occurs in your SurfaceView there is check if onTouchListener is set and if so its onTouch method is called
Can someone please explain the relationship of OnTouchListener and OnTouch across different activities and views.
Touch events on views are invoked if you register any callback to them. SurfaceView extends the View class.
ourSurfaceView.setOnTouchListener(this);
setContentView(ourSurfaceView);
So you just set the ourSurfaceView to the activity as their content view and registered the View.OnTouchListener. That means the abstract method onTouch is invoked on the ourSurfaceView instance and not the activity.
In this code, a OnTouchListener is set for ourSurfaceView, and then the onTouch is called for the activity?!
No, it delegates any touch event from ourSurfaceView instance as their content view to your MainActivity class because you registered the View.OnTouchListener
Just a simple Java example:
public class Main {
public static void main(String[] args){
SurfaceClass surfaceClass = new SurfaceClass();
ActivityClass activityClass = new ActivityClass();
surfaceClass.setOnFartListener(activityClass);
//Touch event :D
surfaceClass.fart();
}
public static class ActivityClass implements SurfaceClass.OnFartListener{
#Override
public void onFart(String kindOfFart) {
System.out.println(kindOfFart);
}
}
public static class SurfaceClass{
private SurfaceClass.OnFartListener onFartListener;
public void fart(){
if(onFartListener != null){
onFartListener.onFart("Huge One!!");
}
}
public void setOnFartListener(SurfaceClass.OnFartListener onFartListener){
this.onFartListener = onFartListener;
}
public interface OnFartListener{
void onFart(String kindOfFart);
}
}}
See touch event below :D
javac Main.java && java Main
I am trying to learn game development in android. First I am trying to appear and disappear an object on screen using game loop for every five second. But I did not get succeed. I have read different tutorials and forums. I applied all things as in tutorials but still object is drawing continuously. It is not disappearing. I a not getting what I am missing? Please guide me.
The complete code is here:
MainGameActivity.java
package com.example.showandhideobject;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainGameActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new MainGamePanel(this));
}
}
MainGamePanel .java
package com.example.showandhideobject;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainGamePanel extends SurfaceView implements
SurfaceHolder.Callback {
private MainGameThread thread;
private ImageObject image;
// private long gameStartTime;
public MainGamePanel(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create the game loop thread
thread = new MainGameThread(getHolder(), this);
Bitmap imageBitMap = BitmapFactory.decodeResource(getResources(),
R.drawable.rose);
image = new ImageObject(imageBitMap, 100, 150);
image.setAppeared(false);
image.setDisappearTime(System.currentTimeMillis());
// make the GamePanel focusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void update() {
Log.i("Image Status::::::::::::: ",
Boolean.valueOf(image.isAppeared()).toString());
if (!image.isAppeared()
&& System.currentTimeMillis() - image.getDisappearTime() >= 5000) {
Log.i("Image Object::::::: ", "Showing");
image.setAppeared(true);
image.setAppearTime(System.currentTimeMillis());
}
if (image.isAppeared()
&& (System.currentTimeMillis() - image.getAppearTime() >= 5000)) {
Log.i("Image Object::::::: ", "Not Showing");
image.setAppeared(false);
image.setDisappearTime(System.currentTimeMillis());
}
}
public void render(Canvas canvas) {
if (image.isAppeared()) {
image.draw(canvas);
}
}
}
MainGameThread.java
package com.example.showandhideobject;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainGameThread extends Thread {
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private MainGamePanel gamePanel;
// flag to hold game state
private boolean running;
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
public MainGameThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
while (isRunning()) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
Log.i("With in :::::::::", "Game Loop");
// update game state
gamePanel.update();
// render state to the screen and draw the canvas on the
// panel
gamePanel.render(canvas);
// gamePanel.onDraw(canvas);
}
} finally {
// in case of an exception the surface is not left in an
// inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
}
ImageObject.java
package com.example.showandhideobject;
import android.graphics.Bitmap;
import android.graphics.Canvas;
public class ImageObject {
private Bitmap bitmap; // the actual bitmap
private int x; // the X coordinate
private int y; // the Y coordinate
private boolean isAppeared;
private long appearTime;
private long disappearTime;
// Constructor for this class
public ImageObject(Bitmap bitmap, int x, int y) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isAppeared() {
return isAppeared;
}
public void setAppeared(boolean isAppeared) {
this.isAppeared = isAppeared;
}
public long getAppearTime() {
return appearTime;
}
public void setAppearTime(long appearTime) {
this.appearTime = appearTime;
}
public long getDisappearTime() {
return disappearTime;
}
public void setDisappearTime(long disappearTime) {
this.disappearTime = disappearTime;
}
/* Method to draw images on Canvas */
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2),
y - (bitmap.getHeight() / 2), null);
}
}
in this part
if (image.isAppeared()) {
image.draw(canvas);
}
you never clear your canvas. What you are doing is actually drawing your image over and over on the same spot.
You probably need to redraw a background in cas isAppeared() is false
Edit
you can also use canvas.save() before drawing the image, and canvas.restore() when you don't want the image anymore.
Don't try to optimise too early, game rendering is usually inefficient as almost always most of the screen is expected to change.
Loop should be:
always draw the background to canvas
always draw all game objects to the canvas, let them decide if they are visible or not which will simplify the MainGamePanel class
finally always display canvas (by copying to the image as you are doing)
To expand on point 2:
/* Method to draw images on Canvas */
public void draw(Canvas canvas) {
if(!isAppeared) return; //let the object decide when it should be drawn
canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2),
y - (bitmap.getHeight() / 2), null);
}
Change the method render in MainGamePanel.java to
if (image.isAppeared() && canvas != null) {
image.draw(canvas);
}
I'm developing a 2d Game using Canvas/Surfaceview and have a problem with scrolling my background image.
Check out the game - http://youtu.be/4Gi5rRqzZ3M
In the NinJump game, the character Ninja is just jumping in X coordinates and Background image is scrolling at a very high speed, making Ninja look like it is actually running.
I have created the basic setup, created the Ninja, added jump functionality, added background. Now I want to repeat the same background over and over again. How can I accomplish that?
Below are my source files - Main Activity Class
package com.abc.apps;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
public class LadderActivity extends Activity {
private static final String TAG = LadderActivity.class.getSimpleName();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// requesting to turn the title OFF
requestWindowFeature(Window.FEATURE_NO_TITLE);
// making it full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// set our MainGamePanel as the View
setContentView(new MainGameBoard(this));
Log.d(TAG, "View added");
}
#Override
protected void onDestroy() {
Log.d(TAG, "Destroying...");
super.onDestroy();
}
#Override
protected void onStop() {
Log.d(TAG, "Stopping...");
super.onStop();
}
}
Game Board extends SurfaceView
package com.abc.apps;
import android.app.Activity;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MainGameBoard extends SurfaceView implements SurfaceHolder.Callback{
private MainGameLoop thread;
private Monkey monkey;
private static final String TAG = MainGameLoop.class.getSimpleName();
int currentX, currentY;
public MainGameBoard(Context context) {
super(context);
// TODO Auto-generated constructor stub
// adding the callback (this) to the surface holder to intercept events
//This line sets the current class (MainGamePanel) as the handler for the events happening on the actual surface
getHolder().addCallback(this);
// create monkey and load bitmap INITIALIZE AT LEFT
monkey = new Monkey(BitmapFactory.decodeResource(getResources(), R.drawable.actor),60, 340);
// create the game loop thread
thread = new MainGameLoop(getHolder(), this);
// make the GamePanel focusable so it can handle events.
setFocusable(true);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
thread.setRunning(true);
thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
}
catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//For jumping Left
if (event.getX() < (getWidth()/2 - 32)) {
// Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
//Log.d(TAG, "Jump Left");
// Sleep so that the main thread doesn't get flooded with UI events.
try {
Thread.sleep(32);
monkey.setX((getWidth()/2 - 60));
monkey.setY(monkey.getY()-70);
} catch (InterruptedException e) {
// No big deal if this sleep is interrupted.
}
}
//For Jumping Right
if (event.getX() > (getWidth()/2 + 32)) {
//Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
//Log.d(TAG, "Jump Right");
// Sleep so that the main thread doesn't get flooded with UI events.
try {
Thread.sleep(32);
monkey.setX((getWidth()/2 + 60));
monkey.setY(monkey.getY()-70);
} catch (InterruptedException e) {
// No big deal if this sleep is interrupted.
}
}
/* //Middle Portion
if (event.getX() > (getWidth()/2 - 32) && event.getX() < (getWidth()/2 +32)) {
//thread.setRunning(false);
//((Activity)getContext()).finish();
}*/
}
return true;
}
public void render(Canvas canvas) {
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.monkey_sc), 0, 0,null);
monkey.draw(canvas);
}
/* #Override
protected void onDraw(Canvas canvas){
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.monkey_scene), 0, 0,null);
monkey.draw(canvas);
}*/
}
Main Game Loop
package com.abc.apps;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainGameLoop extends Thread {
private SurfaceHolder surfaceHolder;
private MainGameBoard gameBoard;
private Monkey monkey;
private static final String TAG = MainGameLoop.class.getSimpleName();
// flag to hold game state
private boolean running = true;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing on the surface
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
// update game state
// render state to the screen
// draws the canvas on the panel
gameBoard.render(canvas);
}
}
finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
public MainGameLoop(SurfaceHolder surfaceHolder, MainGameBoard gameBoard) {
super();
this.surfaceHolder = surfaceHolder;
this.gameBoard = gameBoard;
}
}//MainThread
Monkey Class
package com.abc.apps;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.MotionEvent;
public class Monkey {
private Bitmap bitmap; // the actual bitmap
private int x; // the X coordinate
private int y; // the Y coordinate
private boolean touched; // if monkey is touched
public Monkey(Bitmap bitmap, int x, int y) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isTouched() {
return touched;
}
public void setTouched(boolean touched) {
this.touched = touched;
}
public void draw(Canvas canvas) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y, paint);
}
}
It looks like you are drawing your background in your MainGameBoard class in the render method.
public void render(Canvas canvas) {
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.monkey_sc), 0, 0,null);
monkey.draw(canvas);
}
You should just need 2 drawBitmap calls instead of 1 there.
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.monkey_sc), 0, y_offset1,null);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.monkey_sc), 0, y_offset2,null);
I'm making an assumption that each background image has the same height or greater than the screen height; if it is less than the screen height you would need more than 2 instances.
Then you start 1 image at y_offset1 = 0 and the other at y_offset2 = -image_height.
Each draw you would increase y_offset1 and y_offset2 by the same amount. You would then need to do a check for both offsets to see if either has an amount greater than the screen height. If it does then the y_offset that is now "below screen" should be reset to the other y_offset minus the image_height. This will create a scroll image that loops indefinitely.
When using this type of technique it is important to think about your image edges; the image should be designed such that they tile seamlessly, otherwise at the looping point there is a noticeable visual artifact along the edge.
I am trying to draw a ball to my screen using 3 classes. I have read a little about this and I found a code snippet that works using the 3 classes on one page, Playing with graphics in Android
I altered the code so that I have a ball that is moving and shifts direction when hitting the wall like the picture below (this is using the code in the link).
Now I like to separate the classes into 3 different pages for not making everything so crowded, everything is set up the same way.
Here are the 3 classes I have.
BallActivity.java
Ball.java
BallThread.java
package com.brick.breaker;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class BallActivity extends Activity {
private Ball ball;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
ball = new Ball(this);
setContentView(ball);
}
#Override
protected void onPause() {
super.onPause();
setContentView(null);
ball = null;
finish();
}
}
package com.brick.breaker;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Ball extends SurfaceView implements SurfaceHolder.Callback {
private BallThread ballThread = null;
private Bitmap bitmap;
private float x, y;
private float vx, vy;
public Ball(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ball);
x = 50.0f;
y = 50.0f;
vx = 10.0f;
vy = 10.0f;
getHolder().addCallback(this);
ballThread = new BallThread(getHolder(), this);
}
protected void onDraw(Canvas canvas) {
update(canvas);
canvas.drawBitmap(bitmap, x, y, null);
}
public void update(Canvas canvas) {
checkCollisions(canvas);
x += vx;
y += vy;
}
public void checkCollisions(Canvas canvas) {
if(x - vx < 0) {
vx = Math.abs(vx);
} else if(x + vx > canvas.getWidth() - getBitmapWidth()) {
vx = -Math.abs(vx);
}
if(y - vy < 0) {
vy = Math.abs(vy);
} else if(y + vy > canvas.getHeight() - getBitmapHeight()) {
vy = -Math.abs(vy);
}
}
public int getBitmapWidth() {
if(bitmap != null) {
return bitmap.getWidth();
} else {
return 0;
}
}
public int getBitmapHeight() {
if(bitmap != null) {
return bitmap.getHeight();
} else {
return 0;
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
ballThread.setRunnable(true);
ballThread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
ballThread.setRunnable(false);
while(retry) {
try {
ballThread.join();
retry = false;
} catch(InterruptedException ie) {
//Try again and again and again
}
break;
}
ballThread = null;
}
}
package com.brick.breaker;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class BallThread extends Thread {
private SurfaceHolder sh;
private Ball ball;
private Canvas canvas;
private boolean run = false;
public BallThread(SurfaceHolder _holder,Ball _ball) {
sh = _holder;
ball = _ball;
}
public void setRunnable(boolean _run) {
run = _run;
}
public void run() {
while(run) {
canvas = null;
try {
canvas = sh.lockCanvas(null);
synchronized(sh) {
ball.onDraw(canvas);
}
} finally {
if(canvas != null) {
sh.unlockCanvasAndPost(canvas);
}
}
}
}
public Canvas getCanvas() {
if(canvas != null) {
return canvas;
} else {
return null;
}
}
}
Here is a picture that shows the outcome of these classes.
I've tried to figure this out but since I am pretty new to Android development I thought I could ask for help.
Does any one know what is causing the ball to be draw like that?
The code is pretty much the same as the one in the link and I have tried to experiment to find a solution but no luck.
well , as you can see on the image , you only drew the ball . instead , you need to re-drew a black background (or whatever that you wish) before each time you draw the ball.
alternatively , you can draw a black area only on the previous position , but you might have problems with it later , when you use more objects.
here's a nice sample, similar to what you do
A quick look and I would have to say you are just drawing on the same surface and never requesting your surfaceview to redraw itself. at the end of the finally block, in the IF Statement use: postInvalidate(); That should cause the surface view to redraw itself.
put this
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
.....
}
See how i have done the pendulum simulation at
http://som-itsolutions.blogspot.in/2012/06/android-graphics-and-animation-pendulum.html
You can clone the source code of this project from
https://github.com/sommukhopadhyay/pendulumsimulation
[edit]The answer was wrong, but the comment was helpful so I'll leave this answer up:
Not the question you asked, but there is a problem in your code. In Android you are only allowed to write to the screen in the UI thread. This is the thread that runs all the Activity callbacks, etc. By writing to the screen from BallThread you are risking many odd failures in your program.
there is class, that draws on canvas some field
package com.cerbertek;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class PlayGameView extends SurfaceView implements SurfaceHolder.Callback {
private CanvasThread canvasthread;
private Context mContext;
private Region firstRec;
private class CanvasThread extends Thread {
private SurfaceHolder _holder;
private boolean _run = false;
public CanvasThread(SurfaceHolder surfaceHolder) {
_holder = surfaceHolder;
}
public void setRunning(boolean run) {
_run = run;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _holder.lockCanvas(null);
synchronized (_holder) {
onDraw(c);
}
} finally {
if (c != null) {
_holder.unlockCanvasAndPost(c);
}
}
}
}
}
public PlayGameView (Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
canvasthread = new CanvasThread(getHolder());
setFocusable(true);
}
#Override
public void onDraw(Canvas canvas) {
Paint paint = new Paint ();
Bitmap wrench = BitmapFactory.decodeResource(getResources(), R.drawable.wrench);
canvas.drawColor(Color .BLACK);
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
int left = canvas.getWidth()/2 - wrench.getWidth()*2 + j*wrench.getWidth();
int top = 0 + i*wrench.getHeight();
canvas.drawBitmap(wrench, left, top, null);
Log.d(i + " " + j, left+ " " + top);
}
}
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
canvasthread.setRunning(true);
canvasthread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
canvasthread.setRunning(false);
while (retry) {
try {
canvasthread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
so i want to detect where i click on(for exemple there is 2 rects and i want to detect what rect i click on). i can set setOnClickListener to my view, but how to get position of click?
then i want to set the regions while drawing( it is right, yeah? or not?) and in activity i'll check is click coordinates contains regions
so
1) how can i get coord of click
2) what is the pretty good way to do do all that stuff, because my ideas are poor often
Look at the setOnTouchListener.
The OnTouchListener implements a method with the following signature:
public boolean onTouch(View v, MotionEvent event)
The MotionEvent has information about where the touch actually happened. (event.getX() and event.getY())