Yesterday I asked a question pertaining to Android graphics, and now that I have the basis up and running, I've made a rendering loop inside of my custom view class.
protected void render() {
long startTime = System.nanoTime();
long elapsedTime;
while (Main.running) {
Bitmap image = Bitmap.createBitmap(Main.WIDTH, Main.HEIGHT, null); //creates new Bitmap to draw onto
Canvas canvas = new Canvas(image); //new Canvas with the attached blank Bitmap
long newTime = System.nanoTime();
elapsedTime = newTime - startTime;
if (elapsedTime >= 1000000000/60) { //if enough time has passed for it to update
startTime = newTime;
Main.player.render(canvas); //this renders a test object, see below
}
display = Bitmap.createBitmap(image); //this writes to the display Bitmap that is drawn in onDraw, see below
invalidate(); //invoke onDraw()
}
}
Here is the code for drawing my player image (which DOES work if I draw it once rather than in a loop.)
public void render(Canvas c) {
int xp = (int) x;
int yp = (int) y;
c.drawBitmap(Images.box, xp, yp, null);
}
And inside onDraw:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(display, 0, 0, null);
}
If I just render the player and call invalidate() once, rather than looping it multiple times, it displays fine (and dandy). However, when I put it in the loop block it does not render.
According to my logging, the loop is functional and calls invalidate(), but it never actually enters the onDraw method during the loop.
If anybody could explain why this is happening that would be very grand, and if you need more information please let me know and I will provide.
EDIT:
In my main Activity class, I have a thread for game logic and then enter the render loop with the main thread after starting the logic thread.
Thread update = new Thread() { //game logic thread
public void run() {
loop(); //game logic loop
}
};
update.start(); //start logic thread
surface.render(); //start render loop (surface is the name of my custom view)
I have tried making a separate thread for rendering and entering the game loop with my main thread, however that still did not work.
Related
I'm writing a simple Whack a Mole clone, and I've got my UI elements declared in a GridLayout in a layout.xml, then assigned to ImageView variables in an array programmatically. I've got a startGame() method that simply takes a random int, pulls it from the array and causes it to go visible for a second, then repeats. For some reason, when I put this code in a while() loop, it causes my UI to go blank as soon as it's launched.
I know it's the while() loop because I tried taking the code out of the while() loop, and it ran correctly (once), but turns everything white when placed in a while loop.
Here's the method causing the problem:
public void startGame() {
gameStarted = true;
while(gameStarted) {
randomInt = rand.nextInt(11);
mole[randomInt].setVisibility(View.VISIBLE);
handler.postDelayed(new Runnable() {
#Override
public void run() {
mole[randomInt].setVisibility(View.INVISIBLE);
}
}, 5000);
}
}
All the other relevant code is in onCreate, it's otherwise just a skeleton Activity subclass.
public class WAM_Activity extends Activity {
private ImageView[] mole = new ImageView[11];
private int[] moleId = {R.id.mole1, R.id.mole3, R.id.mole4, R.id.mole5, R.id.mole6, R.id.mole7, R.id.mole8, R.id.mole9, R.id.mole10, R.id.mole11, R.id.mole12};
private boolean gameStarted;
private int randomInt = 0;
private Random rand = new Random();
Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wam_view_layout);
for (int i = 0; i < 11; i++) {
mole[i] = (ImageView) findViewById(moleId[i]);
mole[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do stuff eventually
}
});
}
gameStarted = true;
startGame();
}
Any idea why this isn't working? I've been staring at it for hours and I'm quite stumped.
Android doesn't work that way, when onCreate is called, it need to be finished in order for the app to keep responding, I'm surprised you are not getting any "App not respopnding" error.
If you want to create a "game loop" you can simply by creating a new Thread and putting the while in there.
Activity's lifecycle must be executed without blocking them for the app to operate correctly, for more info check here.
Do you know about threads? if you want i can post an example of how to do that with threads but it might be long and if you don't know what a Thread is it will be too confusing for you.
Edit: Ok I'll make an example of a Thread
When I create my games I usually have only one Activity that the only thing it does is creating a custom SurfaceView and nothing else.
public class GameActivity extends Activity
{
//This is a custom class that extends SurfaceView - I will write it below
private GameSurface game;
#Override
protected void onCreate(Bundle bundle)
{
super.onCreate(bundle);
//Create a new instance of the game
game = new GameSurface(this);
//Set the View of the game to the activity
setContentView(game);
}
}
You can also add extra stuff like onSaveInstanceState to save game data and restore them later but I don't want to add them now so the code looks simple.
This class was very simple, let's move on to our SurfaceView. The reason I picked a SurfaceView to do that it's because it is made to allow custom graphics to be drawn on it - exactly what we want on a video game. I will try to make the class as simple as possible:
/*SurfaceHolder.Callback will run some functions in our class when
our surface is completed - at that point we can initialize data
that have to do with the View's width/height.
I don't know if you've noticed that on a View's onCreate()
when you call getWidth() or getHeight() you get 0, that's because
the surface is not initialized yet, this is a way to fix that.
Also we need a Runnable to run the Thread inside this class,
no need to make more classes and make it more complicated*/
public class GameSurface extends SurfaceView
implements SurfaceHolder.Callback, Runnable
{
//This is our thread - we need the "running" variable to be
//able to stop the Thread manually, this will go inside our "while" loop
private Thread thread;
private boolean running;
//Right here you can add more variables that draw graphics
//For example you can create a new class that has a function that
//takes Canvas as a parameter and draws stuff into it, I will add
//a Rect in this case which is a class already made by android
//but you can create your own class that draws images or more
//complicated stuff
private Rect myRect;
//Rect needs a paint to give it color
private Paint myPaint;
//Constructor
public GameSurface(Context context)
{
super(context);
//This is the callback to let us know when surface is completed
getHolder().addCallback(this);
}
//When a class implements SurfaceHolder.Callback you are forced to
//create three functions "surfaceCreated", "surfaceChanged" and
//"surfaceDestroyed" these are called when the surface is created,
//when some settings are changed (like the orientation) and when
//it is about to be destroyed
#Override
public void surfaceCreated(Surface holder)
{
//Let's initialize our Rect, lets assume we want it to have 40
//pixels height and fill the screen's width
myRect = new Rect(0, 0, getWidth(), 40);
//Give color to the rect
myPaint = new Paint();
myPaint.setARGB(0, 255, 0, 0);
//In case you are not familiar with the Rect class, as
//parameters it gets Rect(left, top, right, bottom)
//Time to start our Thread - nothing much to explain here if
//you know how threads work, remember this class implements
//Runnable so the Thread's constructor gets "this" as parameter
running = true;
thread = new Thread(this);
thread.start();
}
//We won't use this one for now, but we are forced to type it
//Even if we leave it empty
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
//When the surface is destroyed we just want the Thread to
//terminate - we don't want threads running when our app is not visible!
#Override
public void surfaceDestroyed(SurfaceHolder holder)
//We will type this function later
{destroyThread();}
//Time for the interesting stuff! let's start with input
#Override
public boolean onTouchEvent(MotionEvent event)
{
//The logic is as follows: when our Rect is touched, we want
//it to become smaller
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if (myRect.contains((int) event.getX(), (int) event.getY())
{
myRect.right -= 5;
//Return true - we did something with the input
return true;
}
}
return super.onTouchEvent(event);
}
//This is our update, it will run once per frame
private void update()
{
//Let's assume we want our rect to move 1 pixel downwards
//on every frame
myRect.offset(0, 1);
}
//Now for our draw function
public void draw(Canvas canvas)
{
//Here we want to draw a background and our rect
canvas.drawARGB(0, 0, 0, 255);
canvas.drawRect(myRect, myPaint);
}
//The only thing left is our run() function for the Thread
#Override
public void run()
{
//Screen
Canvas canvas;
//Our game cycle (the famous while)
while(running)
{
//Count start time so we can calculate frames
int startTime = System.currentTimeMillis();
//Update our game
update();
//Empty screen so it can obtain new instance
canvas = null;
//Try locking the canvas for pixel editing on surface
try
{
//Try getting screen
canvas = getHolder().lockCanvas();
//Succeeded
if (canvas != null) synchronized (getHolder())
{
//Actual drawing - our draw function
draw(canvas);
}
} finally
{
//Draw changes
if (canvas != null) getHolder().unlockCanvasAndPost(canvas);
}
//End Frame - 1000/30 means 30 frames per second
int frameTime = System.currentTimeMillis() -startTime;
if (frameTime < 1000/30)
try { Thread.sleep(1000/30 -frameTime); } catch (InterruptedException e){}
}
}
//Last but not least, our function for closing the thread
private void destroyThread()
{
//Stop thread's loop
running = false;
//Try to join thread with UI thread
boolean retry = true;
while (retry)
{
try {thread.join(); retry = false;}
catch (InterruptedException e) {}
}
}
}
I may have made some minor mistakes (probably with case sensitive letters) so feel free to correct these, I wrote the code at once so I didn't have time to test it, it should work flawlessly though.
If you have any more questions, need more explanation or something is not working right let me know!
I am quite new to Android and want to create a simple game.
Therefor i need a thread which is drawing a transparent Rectangle on different positions every 4 seconds with a 2 second break (without drawing).
I got it working with a "recursive" thread calling a new instance of itself with handler.postdelayed.
My feeling about threads tells me, that this isn't a very nice way...
While searching here for similar topics, i found out about the Timer construct. Can i use this for my problem?
Is there a better way to do this?
(edit) The thread is meant for highlighting part of the gameboard, but only for 4 seconds. After that there should be 2 seconds without highlighting. Then 4 seconds highlighting the next part of the board etc.
(edit2) I couldn't use sleep, because it froze my UI. If anyone has a similar situation, here is how i solved it:
public class myRunnable implements Runnable {
private int duration;
private int counter;
private boolean highlight;
public myRunnable(int duration, boolean highlight) {
this.duration = duration;
this.highlight = !highlight;
}
#Override
public void run() {
if (highlight) {
// highlight 4s long
highlight();
invalidate();
myThread = new myRunnable(duration, highlight);
postDelayed(myThread, duration);
} else {
// pause (2s)
resetHighlight();
invalidate();
myThread = new myRunnable(duration, highlight);
postDelayed(myThread, noHighlightDuration);
}
}
}
The Best way to do this is use to use invalidate() to call the onDraw() function and a method to update the position. Something like this-
int x,y;
protected void onDraw(Canvas canvas) {
x=10;
y=10;
canvas.drawRect(___);
update();
invalidate();
}
private void update()
{/*Change the x and y/*}
Here everytime inavlidate() is called from anywhere the onDraw() is called again with the new x and y.
My surfaceview onDraw method sometimes skips drawing some bitmaps on the screen and once it skips it never draws the particular bitmaps again.
My code
public class Board extends SurfaceView implements SurfaceHolder.Callback{
//varaibles declared here
public Board(){
//initaializations here
getHolder().addCallback(this);
}
//my onDraw method
protected void onDraw(Canvas canvas){
for(int u = 0;u<6; u++){
ai.get(u).draw(canvas);//each of these objects draws something on the screen.
human.get(u).draw(canvas);
}
postInvalidate();
}
public void surfaceCreated(SurfaceHolder holder){
gameLoop = new GameLoop(this);
gameLoop.start();
}
The onDraw() method is called every 100milliseconds in a thread that runs the game loop.
public class GameLoop extends Thread{
Board board;
private final int DELAY = 100;
public GameLoop(Board board){
this.board=board;
}
protected void run(){
long beforeTime,timediff,sleep;
beforeTime = System.currentTimeMillis();
while(running)
{
Canvas c = null;
try{
c=board.getHolder.lockCanvas();
synchronized(board.getHolder()){
board.onDraw(c);
}finally{
if(c!=null)
board.getHolder.unlockCanvas(c);
}
timeDiff = System.currentTimeMillis - beforeTime;
sleep = DELAY - timeDiff;
if(sleep<0)
sleep = 10;
try{
Thread.sleep(sleep);
}catch(InterruptedException e){}
beforeTime = System.currentTimeMillis();
}
}
So this skips happen like this: sometimes(in the onDraw() method) when u=0 does not draw or u = 5, it could be u = any of the possible values and the rest draws and once it skips that values it keeps skipping it everytime the onDraw method is called. I hope I have been able to make it clear enough.
I would appreciate help to resolve this problem. Thanks
As is, you are updating your main UI thread from another thread. Android docs indicate that this leads to unpredictable behavior, which you are experiencing.
Consider subclassing AsyncTask to do your threading for you. There are routines for doing background work and posting to the main UI thread from the UI thread itself.
Basically, you call execute on the task, which then calls it's doInBackground routine and when it is done, it calls onPostExecute routine with the results of the task.
These sources should be useful in helping to understand the problem and subclass AsyncTask:
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html
http://developer.android.com/reference/android/os/AsyncTask.html
I have a live wallpaper which I created using the android canvas. Upon testing, I felt it necessary to harness the power of OpenGL, and so am experimenting with AndEngine. I am wondering how I can achieve the following.
I have a background image that fills the whole screen, with many smaller bitmaps floating over the top (not animated movements)
So far I have this for the background image:
#Override
public void onLoadResources()
{
mtexture = new Texture(1024, 1024, TextureOptions.BILINEAR);
TextureRegionFactory.setAssetBasePath("gfx/");
mtextureRegion = TextureRegionFactory.createFromResource(mtexture , this, R.drawable.background1, 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mtexture );
}
#Override
public Scene onLoadScene(){
final Scene scene = new Scene(1);
Sprite background = new Sprite(0, 0, CAMERA_WIDTH*2, CAMERA_HEIGHT, mtextureRegion )
SpriteBackground sb = new SpriteBackground(background);
scene.setBackground(sb);
scene.setBackgroundEnabled(true);
return scene;
}
This works fine for the background, but I require moving sprites.
In my canvas code, I do the following to update the position & physics of the moving objects and draw the canvas every few ms
private final Runnable drawScreen = new Runnable() {
public void run() {
drawFrame();
}};
-
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
//draw
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
mHandler.removeCallbacks(drawScreen);
mHandler.postDelayed(drawScreen, 10);
}
What is the appropriate way to do this on AndEngine? do I use the same code and substitute openGL calls?
I had a look at GLEngine, am I supposed to send Runnables to the GlThread queue?
EDIT - I think I found the answer...an UpdateHandler. But how can I inform the handler of an update (i.e. to call the onUpdate method). If I make a timed Handler, what happens if I call too often, does a queue of requests build up?
First of all, don't use the constructor Scene(int), it's deprecated. Use Scene() instead.
Correct, you should use an update handler.
You can create an UpdateHandler, and then register it to your scene:
scene.registerUpdateHandler(mUpdateHandler);
This way, the code in mUpdateHandler.onUpdate method is executed each time the scene updates (Each frame.). You don't call it manually. If you want to stop it, call:
scene.unregisterUpdateHandler(mUpdateHandler);
So, the onUpdate method is always executed in the UpdateThread, so you can be sure you can do any change to entities you want there. So you can move around and sprite you want, etc...
By the way, why is the background's width CAMERA_WIDTH*2? It means that only the left half of your sprite is shown. If you don't plan moving the camera, then the right half won't ever show.
I'm writing a little game as part of a programming assignment. I've got my sprites/images moving the way I want to, except they're very jerky. Here's how I'm loading up the image:
//missile and paint are private members of the animation thread
missile = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.missile));
paint = new Paint();
paint.setAntiAlias(true);
startTime = new Date().getTime();
The run method of my thread is like this:
#Override
public void run() {
while(running) {
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
updatePhysics();
doDraw(canvas);
}
} finally {
if(canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
updatePhysics simply performs calculations and stores the value inside a private member of the thread called projectilePosition:
function updatePhysics() {
projectilePosition = interceptionCalculationService.positionAtTime((startTime - new Date().getTime()) / 1000);
}
Then, in my doDraw method I do:
private void doDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
...
//I update the position of the missile
canvas.drawBitmap(missile, (int) projectilePosition.getDistance() * 2, (int) (getHeight() - (95 + projectilePosition.getHeight())), paint);
canvas.restore();
}
The problem is that the animation is extremely jerky. The missile follows the correct path, but it's not smooth at all. I assume that's because I don't really control the interval when the thread's run method is called. How can I make my animation more smooth? I was looking at TranslateAnimation, but I can't figure out how to use it.
This is my first time writing a game and doing graphics/animation, so I'm not well aware of the best practices or even the tools. I got this far by looking up numerous tutorials.
Yes the doDraw method is called as fast as the processor can handle and operating system allows, which means it is affected by other processes and also means it is using a ton of processor time. As a fairly simple but not recommended solution add Thread.Sleep(50) in your while loop. Look up how to use timers for animation.
My solution was to stop relying on the system time and to use a variable (that is a private member of the thread) called currentTime that gets incremented in either doDraw or updatePhysics. So updatePhysics becomes:
function updatePhysics() {
projectilePosition = interceptionCalculationService.positionAtTime(currentTime);
currentTime++;
}
currentTime is initialized to 0 in the constructor of the thread. This made my animation smooth.