I have a game which displays an array of colored blocks. The user can move these blocks around the screen. When a touch event occurs, I take note of the cell that has been touched (cell[i][j].isMoving = true). If the user moves the block around I draw the rectangle relative to an offset value. When a touch up event is detected, I check whether or not the user has dragged the block far enough to signify a moving of a block.
My basic draw loop is as follows:
for(int i = 0; i < xCells, i++){
for(int j = 0; j < yCells; j++){
if(cell[i][j].isMoving)
canvas.drawRect(...) // draw with offsets
else
canvas.drawRect(..)
}
}
The problem I am having is when a user releases the block it occasionally flickers briefly.
When a touch up event occurs, the offset must be set to 0, the coordinates of the block changed (if requirements are met) and isMoving has to be set to false.
As I have a thread constantly running that calls the draw code above, it appears that the UI thread is altering the array of blocks, meaning it is in an inconsistent state when the draw method occurs.
Does anyone have suggestions on how to fix this? Could I use a handler? I've tried synchronizing the onTouchEvent method and the onDraw method, but this seems to occasionally block user input
thanks
The safe solution is to only allow one thread to create objects, add and remove them from a List or array after the game has started.
I had problems with random AIOOBEs (Array Index Out Of Bounds Exception) errors and no synchornize could solve it properly plus it was slowing down the response of the user.
My solution, which is now stable and fast (never had an AIOOBEs since) is to make UI thread inform the game thread to create or manipulate an object by setting a boolean flag and coordinates of the touch into simple variables.
Since the game thread loops about 60 times per second this proved to be sufficent to pick up the message from the UI thread and do something about it.
In your case onTouch event should never alter cell[i][j] directly but only set some integer to a particluar value. Then your thread would at some point evaluate this variables and set cell[i][j] and then draw, so onTouch can never unpredictably interfear with your thread.
This is a very simple solution and it works great...
Related
I have a group of objects within a game that is a sensor(let's call them collectively object1). When it collides with my main object (object2), the score increments by 10. However, after each subsequent restart of the scene(not the whole game), each collision between object1 and object2 duplicates itself, then triplicates and so forth.
So for the first running of the scene, object1 collides with object2 once(I know this because I have a print statement every time the 2 objects collide). The second time it collides twice, the third time three times and so forth. I presume I am not removing a certain feature of the sensor but I cannot figure out what it is. How do I correctly remove the objects if this is the problem?
My code for the removal of object1 on collision:
local function onCollision( self,event )
if(event.object2.name == "bonus")then--if we hit a bonus ball
event.object2:removeSelf()--removes the bonus ball object from the scene
print("bonus collided")
display.remove(event.object2)
game.addToScore(10)--adds a value of 10 to the score
scoreText.text = game.returnScore()
Runtime:removeEventListener("enterFrame", event.object2)
else
composer.gotoScene("restart")
Runtime:removeEventListener("touch", onObjectTouch)
end
end
I studied your code and in short this is the problem
ball.collision = onCollision
It's located under scene:show and therefore a new eventListener will be created each time that you to the scene. And it's not being removed under scene:hide.
Basically half of your code doesn't really do anything. For exampel you remove the same item twice, add Runtime listeners to objects (not functions) and you're trying to remove thise Runtime listeners. For example this tries to remove a Runtime event listener on a display object that doesn't even exist:
Runtime:removeEventListener( "collision", circle )
What you need to do is:
Create all objects and listeners under scene:create
Use local collision handling (i.e. not Runtime listeners): https://docs.coronalabs.com/daily/guide/physics/collisionDetection/index.html#local-collision-handling
Rewrite your code so that you have functions and listeners outside of the scene:create
I'm having an issue with an android application.
I have a thread that constantly iterators over a list or shapes, updates their positions, and sometimes removes an item from the list. At the end of the thread's while loop it calls postInvalidate() to prompt a repaint.
Here is the code where modification to the ArrayList happens.
Iterator<Shape> iterator = this.myList.iterator();
while (iterator.hasNext()) {
Shape nextShape = iterator.next();
nextShape.move();
if(condition) {
iterator.remove()
}
}
The onDraw method uses a for each loop to draw each of those items. I am getting concurrent modification in the onDraw method despite only modifying the list through the iterator. I've tried CopyOnWriteArrayList and Collections.synchronized with the same results.
Any help would be appreciated.
There's a couple of ways to solve this. One would be to use a synchronized block in the thread and in onDraw, but that would prevent the 2nd thread from running at the same time as the UI thread.
I think the best way is for you to use two collections. Your code should look like this
onDraw(){
synchronized(myList){
//Do your draw code. Get the iterator inside this block
}
}
threadFunction(){
List threadList;
while(1){
//Update threadList, which holds all the objects
synchronized(myList){
myList = threadList.copy(); //Do a deep copy, don't just copy over the reference
}
postInvalidate();
}
}
This will make your draw function operate on a copy of the list made at the end of an iteration. If you're working on a deep copy, you won't get any synchronization problems as the thread isn't altering the same list. The synchronized blocks will stop the thread from overwriting the draw function's copy during a draw. The only remaining issue is that the overwrite of the list in the thread will hang until a draw is done, but since updating it more frequently than that won't show on screen anyway, my guess is that's acceptable.
On developing a painting canvas application in android, i need to track all the points and have to redraw it in another canvas. Now i am able to track all the points, but don't know how to synchronize the point drawing in case of draw and redraw ie the user should redraw the points at the same time gap as in the draw. How can i achieve this?
Not sure if this is the sort of answer you are looking for but I would record the events with a sort of timestamp, really a time difference to the next point. Something like:
class Point {
int x;
int y;
long deltaTime;
}
Its up to you how precise you want to be with the timing. Second to millisecond precision should be good enough. You could interpret deltaTime as either the time until this point should be drawn or the time until the next point should be drawn (I'm going to use the latter in my example).
A few reasons to use a deltaTime instead of a direct timestamp is that it lets you check for really long pauses and you are going to have to compute the delta time anyways in playback. Also using it as a long should give you enough room for really lengthy pauses and lets you use the Handler class which accepts a long integer for the number of milliseconds to wait before executing.
public class Redrawer implements Handler.callback {
LinkedList<Point> points; //List of point objects describing your drawing
Handler handler = new Handler(this); //Probably should place this in class initialization code
static final int MSG_DRAW_NEXT = 0;
public void begin(){
//Do any prep work here and then we can cheat and mimic a message call
//Without a delay specified it will be called ASAP but on another
//thread
handler.sendEmptyMessage(MSG_DRAW_NEXT);
}
public boolean handleMessage(Message msg){
//If you use the handler for other things you will want to
//branch off depending on msg.what
Point p = points.remove(); //returns the first element, and removes it from the list
drawPoint(p);
if (!points.isEmpty())
handler.sendEmptyMessageDelayed(MSG_DRAW_NEXT, p.deltaTime);
public void drawPoint(Point p){
//Canvas drawing code here
//something like canvas.drawPixel(p.x, p.y, SOMECOLOR);
//too lazy to look up the details right now
//also since this is called on another thread you might want to use
//view.postInvalidate
}
This code is far from complete or bullet-proof. Namely you will need to possibly pause or restart the redrawing at a later time because the user switched activities or got a phone call, etc. I also didn't implement the details of where or how you get the canvas object (I figure you have that part down by now). Also you probably want to keep track of the previous point so you can make a rectangle to send to View.postInvalidate as redrawing a small portion of the screen is much faster than redrawing it all. Lastly I didn't implement any clean-up, the handler and points list will need to be destroyed as needed.
There are probably several different approaches to this, some probably better than this. If you're worried about long pauses between touch events simply add a check for the deltaTime if its greater than say 10 seconds, then just override it to 10 seconds. Ex. handler.sendEmptyMessage(MSG_DRAW_NEXT, Math.min(p.deltaTime, 100000)); I'd suggest using a constant instead of a hard coded number however.
Hope this helps
I'm trying to move a ball on canvas. a and b are similar to x,y coordinate positions. Any way from my code im trying to get different values dynamically. The a,b are global variables. But it seems that "invalidate()" or the refreshing of screen only happens afer end of the whole loop. Do you know why?. And if i have to build this on another thread please suggest me with some simple codes.
private void shootBall(){
while (a>b){
a = getPositionX();
b = getPositionY();
invalidate();
}
}
}
I think it's more correct to say that you can call invalidate() from within a loop, but that that invalidation will not be handled (the canvas won't be redrawn) until after your loop is complete. The problem is that you are calling invalidate on the same thread (the UI toolkit thread) as the one that would call your onDraw() method. So unless/until you hand control back to the toolkit, it cannot possibly do the rendering. So your invalidate() call does actually invalidate the view ... but the view won't be redrawn until after your loop completes and your function returns.
It is more correct to change the position in some function that is called via some timer (which is essentially what the animation classes do). In that function, you would change the values, invalidate() appropriately, and return. Then the toolkit re-renders the scene and your function will get future callbacks and update the position accordingly.
do it like this, and use postInvalidate() instead:
private void shootBall(){
new Thread(new Runnable() {
public void run() {
while (a>b){
a = getPositionX();
b = getPositionY();
postInvalidate();
}
}
}).start();
}
edit: but as mentioned before, don't assume that the invalidate redraws the screen, it marks it as to-be-redrawn and the UI thread will get around to it.
You can put invalidate() at the end of onDraw() like in this example: How can I use the animation framework inside the canvas?
However this works well on some devices while bottlenecks and slows down on other.
To use a thread and SurfaceView go through all of these tutorials: http://www.droidnova.com/playing-with-graphics-in-android-part-i,147.html
UI cant be modified form any new thread..you should use invalidate() in the same thread where your view
Just want to start out by saying this seems like a great site, hope you guys can help!
I'm trying to use the structure laid out in LunarLander to create a simple game in which the user can drag some bitmaps around on the screen (the actual game is more complex, but that's not important). I ripped out the irrelevant parts of LanderLander, and set up my own bitmap drawing, something like
BoardThread (an inner class of BoardView):
run()
{
while(mRun)
{
canvas = lockSurfaceHolder...
syncronized(mSurfaceHolder)
{
/* drawStuff using member position fields
in BoardView */
}
unlockSurfaceHolder
}
}
My drawStuff simply walks through some arrays and throws bitmaps onto the canvas. All that works fine. Then I wanted to start handling touch events so that when the user presses a bitmap, it is selected, when the user unpresses a bitmap, it is deselected, and if a bitmap is selected during a touch move event, the bitmap is dragged. I did this stuff by listening for touch events in the BoardView's parent, BoardActivity, and passing them down into the BoardView. Something like
In BoardView
handleTouchEvent(MotionEvent e)
{
synchronized(mSurfaceHolder)
{
/* Modify shared member fields in BoardView
so BoardThread can render the bitmaps */
}
}
This ALSO works fine. I can drag my tiles around the screen no problem.
However, every once in a while, when the app first starts up and I trigger my first touch event, the handleTouchEvent stops executing at the synchronized line (as viewed in DDMS). The drawing loop is active during this time (I can tell because a timer changes onscreen), and it usually takes several seconds or more before a bunch of touch events come through the pipeline and everything is fine again.
This doesn't seem like deadlock to me, since the draw loop is constantly going in and out of its syncronized block. Shouldn't this allow the event handling thread to grab a lock on mSurfaceHolder? What's going on here? Anyone have suggestions for improving how I've structured this?
Some other info. This "hang" only ever occurs on first touch event after activity start. This includes on orientation change after restoreState has been called. Also, I can remove EVERYTHING within the syncronized block in the event handler, and it will still get hung up at the syncronized call.
Thanks!
So Rob Green from rbgrn helped me get a little more info on this. I ended up using a concurrent queue to deliver touch events to my game thread, but I still had hanging issues when saveInstanceState got called, so I'm now calling Thread.sleep(16) at the bottom of every iteration of my game thread's while loop. Full discussion here.