I'm just picking up android development to make a game. Touched it before, but only picked up the basics. I'm a bit confused how to set up a main loop. I've been into XNA (C#) and I love the separated update/draw loop.
I was wondering how a typical android gameloop works? I've searched online and came across 2 methods:
public void run() {
while (running) {
//Method 1: update is called here
view.update();
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
//Method 2: update is called inside view.onDraw
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
}
}
Let's take the updating of game entities as an example for the 2 methods:
//METHOD1
public void onDraw(Canvas canvas)
{
for (GameEntity entity : entities)
{
entity.update();
entity.draw(canvas);
}
}
//END METHOD 1
//METHOD 2
public void update()
{
for (GameEntity entity : entities)
{
entity.update();
}
}
public void draw(Canvas canvas)
{
for (GameEntity entity : entities)
{
entity.draw(canvas);
}
}
//END METHOD 2
Now I have no experience with threads whatsoever, so I have no idea how XNA does the update/draw loops behind the screens in xna.
But using method 1, I would have to loop through all the entities twice, once for updating and another time for seperate drawing. I'm afraid this will kill the performance, but I háve seen this in samples online.
Am I missing something or am I right and is method 2 the best performance wise?
It matters not how many times you loop since it only matters how many actions you do. And the amount of actions done are basicly the same. since the second "for" only adds an one more supposed "if" for each entety. So its not much.
But it gives you the ability to do only one of the actions and not forced to do both.
for example: if I want the game to update 60 times per sec but only draw 40 fps, I can only do that in method 2. This allows you to have a more fluid game with less calculations, but only if you use it right.
If you have the darw and update happen at the same rate, then it is stupid to split them
Related
My first attempt at AndroidPlot. The data I want to plot (and update every 5 seconds when a new data point arrives) comes from an ArrayBlockingQueue of up to 720 timestamped points. I have a class that implements the XYSeries and PlotListener interfaces. It has a method updatePlotData that just extracts the data from the queue into an array:
class TempPlotSeries implements XYSeries, PlotListener {
private final static String TAG = TempPlotSeries.class.getSimpleName();
private Pair<Date, Float>[] plotArray;
void updatePlotData( ArrayBlockingQueue<Pair<Date, Float>> dataQueue ) throws InterruptedException {
synchronized ( this ) {
wait(); // don't update data until we're notified that current plot is done (& we can get lock)
plotArray = dataQueue.toArray( new Pair[0] );
if( DEBUG ) Log.d( TAG, "updatePlotData run with " + plotArray.length + " data points" );
notifyAll(); // release lock & let other threads know they can continue
}
}
// XYSeries implementation
#Override
public int size( ) {
return plotArray.length;
}
#Override
public Number getX( int index ) {
return (index - HISTORY_BUFFER_SIZE) / (60/TEMP_UPDATE_SECONDS); // e.g., -60 minutes at left edge of graph, -1/12 min at right
}
#Override
public Number getY( int index ) {
return plotArray[index].second; // the temp value
}
#Override
public String getTitle( ) {
return "Temp History";
}
// PlotListener Implementation
#Override
public void onBeforeDraw( Plot source, Canvas canvas ) {
synchronized ( this ) {
try {
wait(); // wait for data updating to finish if it's in progress on another thread
} catch ( InterruptedException e ) {
// unlikely to be interrupted?
}
}
}
// between these 2 calls the plot is redrawn
#Override
public void onAfterDraw( Plot source, Canvas canvas ) {
synchronized ( this ) {
notifyAll( ); // plot done, OK to update data
}
}
}
I don't have much experience with synchronization--does this look reasonable?
My plot setup is:
tempHistoryPlot = (XYPlot) findViewById(R.id.temp_history);
tempPlotSeries = new TempPlotSeries();
tempHistoryPlot.setRenderMode( Plot.RenderMode.USE_BACKGROUND_THREAD );
tempGraphFormatter = new LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);
tempHistoryPlot.addSeries(tempPlotSeries, tempGraphFormatter);
tempGraphWidget = tempHistoryPlot.getGraph();
(couldn't find any documentation on the purpose of getGraph() so don't know if I need it.)
I have an Observable (RxJava) that emits the entire data queue when a new sample is available (every 5 seconds). If the queue is full I discard the oldest value. Then I have:
tempPlotSeries.updatePlotData( newTempHistory );
tempHistoryPlot.redraw();
But the plot isn't drawn. When the app first launches, the "dummy" plot appears in its View, but as soon as I try to draw the plot the entire ConstraintLayout containing the XYPlot element (and other UI elements) is completely blanked. What's going on here?
Other questions: it's my understanding that any code affecting the Android UI must run on the main thread. But we're using a background thread to render the plot. How does this work? Do I perhaps need to insert a .observeOn( AndroidSchedulers.mainThread() operator in my Observable chain?
I don't have much experience with synchronization--does this look reasonable?
I don't think you need the wait() inside the synchronized block inside updatePlotData. You can also use SimpleXYSeries as a reference for how to setup synchronization of this sort.
When the app first launches, the "dummy" plot appears in its View, but as soon as I try to draw the plot the entire ConstraintLayout containing the XYPlot element (and other UI elements) is completely blanked.
I'm having trouble visualizing this. Could you add a screenshot of the "dummy" plot and the subsequent blank plot?
it's my understanding that any code affecting the Android UI must run on the main thread. But we're using a background thread to render the plot. How does this work?
The general rules of using the main thread to update the UI still exist, Androidplot is just using a technique to minimize the main thread usage during intensive rendering: A background thread is used to fill a bitmap buffer with the data to be shown, then notifies the main thread when the buffer is ready to be displayed.
Somewhat Unrelated Suggestion: Looking at your TempPlotSeries implementation, I notice that you are modeling your data as a Pair<Date, Float>[] but your getX() implementation does not make use of the Date part. It appears you're trying to model your data using what I assume is your desired display format for your domain, ie. -60 to -1/12 minutes. For simplicity I'd suggest making getX() return the Date's long epoch value instead. You can apply a display format to these values later.
I'm developing a relatively small 2D game for Android right now. To process the collision detections as efficient as possible, I've created multiple threads working on the calculations:
Thread #1: Main handling of the frames, limiting them to X per second, handling the Bitmaps (rotate, draw...)
Thread #2: Calculate some collisions
Thread #3: Calculate other collisions
What I need is some sort of synchronization, but I am unsure of what's the best way to achieve this. I thought of something like this:
Thread #1:
public class Thread1 imlements Runnable {
public static ArrayList<Boolean> ResponseList = new ArrayList<Boolean>();
static {
ResponseList.add(0, false); // index 0 -> thread 1
ResponseList.add(1, false); // index 1 -> thread 2
}
public void run() {
boolean notFinished;
while(!isInterrupted() && isRunning) {
notFinished = true;
// do thread-business, canvas stuff, etc, draw
while(notFinished) {
notFinished = false;
for(boolean cur: ResponseList) {
if(!cur) notFinished = true;
}
// maybe sleep 10ms or something
}
}
}
}
And in the other calculation threads something like:
public class CalcThread implements Runnable {
private static final INDEX = 0;
public void run() {
while(isRunning) {
ResponseList.set(INDEX, false);
executeCalculations();
ResponseList.set(INDEX, true);
}
}
}
Or would it be faster (as this is what I'm concerned about) to use a Looper/Handler combination? Just read about this, but I'm not sure yet how to implement this. Would look deeper into this is this would be the more efficient method.
I don't know if it is going to be faster, but it will be more reliable for sure. For example, you are using ArrayList from multiple threads without serialization and ArrayList is not thread-safe
Handler is just one of the available mechanisms, I would recommend you to study java.util.concurrent - there is no point in reinventing the wheel, many synchronization primitives are already available. Perhaps Future would work for you
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.
I'm developing a small game on Android and I have a question. I want my main game loop to be the thread that is called from the SurfaceView but every time I want to call a method of another class from this thread I get the "stopped unexpectedly" message.
To be more clear:
#Override
public void run() { //Game Loop
Canvas canvas = null;
while (mRun) {
canvas = mHolder.lockCanvas();
if (canvas != null) {
grub.updateGrubber(); //here I want to update state.
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
the line grub.updateGrubber(); show the error that I mention before. I tried different methods from that class and all of them gives me the same error.
Any help will be appreciated.
If my guess is correct, you are trying to update the UI thread directly. You can't call UI methods from threads other than the main thread. Try:
Runnable action = new Runnable() {
#Override
public void run() {
grub.updateGrubber();
} };
this.post(action);
instead of your `grub.updateGrubber();` line.
I'm actually tested what you suggested and is not a bad idea, but the problem in my code was that I never really created a Grubber object (I miss the = new Grubber(); part).
Probably a mistake related with the enormous amount of code writing and testing that I've done in the past few weeks, sorry about that. Anyway I'm going to use as main game loop the thread that is created to update the canvas in the surfaceview according to what i've read here.
All examples of the use of a SurfaceView seems to use a run method that performs a busy loop. Is that a valid way to do this? All the code I can see follows this paradigm from the lunar lander sample. However, creating a busy while loop seems to be a strange way to code multi threaded apps. Shouldnt the drawing code wait on a queue of drawing commands, or something similar. I would have implemented it that way, but the amount of code that I see that does is like below makes me ask the question... What is the best semantics for a thread drawing on a SurfaceView.
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// DO DRAWING HERE
}
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
I don't know what is best practice in this case, but I have successfully used a slightly modified version of that example in my apps. Since I respond to touch input (rather than continuously updating the canvas) I added a flag to test if drawing even needs to be done. I also added a sleep after each refresh to limit system load. This is my code inside of the try block:
if(mPanel.needsRefresh()) {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
mPanel.onDraw(c);
}
} else {
SystemClock.sleep(10);
}
I'm making a game that displays some numbers on a canvas (score, time, etc).
The way that I currently do this is with the drawtext command on a canvas
// score is some int
draw(Canvas c) {
c.drawText(score+"", x, y, paintSyle);
}
I hear that object creation and garbage collection are expensive operations, and I think this is creating a new string every time it is called.
Right now my game with all bitmap drawing and everything jumps around from 25 to 60 fps. I'd like it to stay closer to the higher number and I'm trying to find ways to speed it up.
Would it be faster/better to make(or find?) some mutable subclass of string and work around this problem? Is there another way to solve this issue? Or is this just how it is?
Introduce two new private member variables String renderedScoreString and int rederedScore and rewrite your draw()-method like that:
draw(Canvas c) {
if (this.score != this.renderedScore || this.renderedScoreString == null) {
this.renderedScore = this.score;
this.renderedScoreString = Integer.toString(this.renderedScore);
}
c.drawText(this.renderedScore, x, y, paintStyle);
}
that should save you a lot! of object creations. You could also hide the boilerplate code behind a getter method, e.g. String getScoreString() which does the same, so you don't have it in the draw()-method.
A friend of mine tipped me in on a solution to this problem. When you want to draw something over time, one of the best (and simplest) mechanisms to do so is to split up what you need to do into two completely separate processes.
ie. Only use the draw command exclusively for drawing stuff, keep logic/assignment in Draw() to an absolute minimum.
private final long TIMER_PERIOD = 500;
private String timeString;
private Runnable updateRunnable;
private Handler updateHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
updateRunnable = new Runnable() {
#Override
public void run() {
timeString = GetTimeString();
updateHandler.postDelayed(updateRunnable, TIMER_PERIOD);
}
}
}
Draw(Canvas c) {
c.drawText(timeString, x, y, paintStyle);
}
In this example the Draw command simply takes timeString in its current state and draws it to the screen. This is highly efficient use of the draw function as it does not require any object creation, and no logic is present that is not immediately required for any drawing to occur. . In the background a Runnable is executing the run() function every 500 miliseconds (approximately). Simply update the Run() function with whatever logic you need to calculate the time (example has a dummy function GetTimeString())
I hope this is helpful.
I know I'm resurrecting a dead thread, but there is one extra optimisation you can add to this which restricts String creation to a one-time thing and thus only triggers the GC once at the start and not during the game (which is quite important for an android game).
Somewhere during the start of your game (onCreate, onResume, as part of a singleton during application startup, etc) create a large String[] which can hold the maximum score (my game fills an array of 10000, so the max score would be 9999). Then loop over it with a for loop, filling each index with a String.valueOf(i).
for (int i = 0; i <scoreStrings.length; i++)
{
scoreStrings[i] = String.valueOf(i);
}
Now, when you need to draw the score, just use the int you use to store the score in as an index to that array, and "hey, presto!", you get the correct string for your score.
canvas.drawText(scoreStrings[score], x, y, paint);