I've been raking my brains on this one for a while.
when I'm running my application (opengl game) eveyrthing goes fine but when I touch the screen my application slows down quite seriously (not noticeable on powerful phones like the nexus one, but on the htc magic it gets quite annoying).
I did a trace and found out that the touch events seem to be handled in a different thread and even if it doesn't take so much processing time I think androids ability to switch between threads is not so good...
What is the best way to handle touch when speed is an issue ?
Currently I'm using :
in the GLSurfaceView
#Override
public boolean onTouchEvent(MotionEvent event) {
GameHandler.onTouchEvent(event);
return true;
}
Any ideas are welcome
I have a feeling the bug report below may be relevant. Sadly in seems its only going to be fixed in gingerbread.
Bug report
Have just seen that SO prefers details in the answers in case links vanish etc. the above refers to a google accepted bug in Adnroid 2.1 targetted for a fix in the gingerbread release.
Issue 7836: system_server consumes excessive CPU processing touch events
I can't vouch for this myself, but my research has shown that touching the screen fires so many events that it floods the events queue and therefore lags the CPU taking your resources.
Try putting: -
try {
Thread.sleep(16);
} catch (InterruptedException e) {} //ignore
before any returns that you have in the onTouch method (usually there's only the one at the end - but just making sure). I know sleep is usually a very bad thing to do, but its not in the UI thread so it should be ok. Sleep 16 should cap the FPS to 60.
Don't put heavy computation to your onTouchEvent(). The OnTouchEvent can be fired tens or hundred of times per second per finger, you should defer the heavy computation to the other part of your game (e.g. the physics engine or the graphic engine). In particular avoid drawing in your onTouchEvent.
You should preferrably use onClickEvent or other less intensive mouse event and only use onTouchEvent when you really need to track the motion of the touch.
#Jason:
This was going to be a comment, but it grew too big and it is a different [better] answer to the one I just gave.
I have changed my implementation using this method as described here
http://obviam.net/index.php/the-android-game-loop/
By employing the method described above means you should not need to sleep the OnTouch events.
Also don't forget to protect your game loop thread as mentioned here http://wonton-games.blogspot.com/2010/06/lunar-lander-resume-game-workaround.html
Also also keep in mind that Chris Pruett when writing Replica Island said he used 2 threads, one for the update() and the other for render() - both will have to be protected.
Chris does sleep his OnTouch with 16 milliseconds (60 fps) to cut back the events - so I would say its best to experiment if you still need to cut the events down - you need only touch the screen whilst stuff is happening to see if it lags and thus subsequently speeds up when let go.
For my needs currently I am using the same thread as I haven't progressed into OpenGL, I still use the canvas. But when I go OpenGL, it will be 2 threads, and each will be a complete class of its own.
Finally, I don't time my sprites by counting frames, I've wrapped them with a timer. I'll share my Class and show you how I call it. Bear in mind I am still new to Java so apologies for poor code.
package com.yourname.yourapplication;
//Used for doing something after a set time
public class TimeDo {
private int mRepeat = 0; //Stores the last wait period for the reset() later
private long mTime = 0; //The goal time of when its due
private boolean mFlagged = false; //Stop them getting a second true on a subsequent check
public TimeDo(int milliseconds) {
reset(milliseconds);
}
public TimeDo() {
this(0);
}
public void reset(int milliseconds) {
mRepeat = milliseconds;
mTime = System.currentTimeMillis() + milliseconds;
mFlagged = mRepeat==0; //ignore if zero
}
public void reset() { //Set it back to the delay used last time
reset(mRepeat);
}
public boolean check() {
if (mFlagged) //Assert: shouldn't really happen
return false;
mFlagged = System.currentTimeMillis() > mTime;
return mFlagged;
}
public boolean checkAndReset() {
if (check()) {
reset();
return true;
}
return false; //note mFlagged could be true here, so don't use it
}
}
And it is implemented thusly: -
public class Gem {
private TimeDo mMoveGem = new TimeDo(100); //move 10 times a second, 100ms
private int mX = 0;
private int mY = 0;
private int mMoveX = 3;
private int mMoveY = 4;
.
.
.
public void update() {
if (mMoveGem.checkAndReset()) {
mX += mMoveX;
mY += mMoveY;
.
.
}
}
public void render(Canvas canvas) {
//etc etc
.
.
}
}
Hope any of that helps!
Sorry if you have to rewrite lots of your application - I did.
Edit: That TimeDo class is not a postDelayed runnable, like an automatic alarm. If you don't check it no "event" will fire. You could get excited and have it create a runnable and pass it a callback method that resides within your class (think of an OnClick method) - but defeats the purpose of running an exclusive time-able update() thread that updates all your components.
Related
In my single page, there were so many textures those I require to render before I was just facing problem in just screen opening but this I was able to resolve this suggestion:
OnBecomeVisible for Canvas UI Objects
Here is the actual code that I used for this purpose:
public void OnValueChangeRefreshCards (Vector2 valueChange)
{
elapsedYDist += valueChange.y;
if (elapsedYDist > 3f) {
elapsedYDist = 0f;
for (int i = 0; i < GameConstants.TOTAL_ROSE_COUNT; i++) {
Card card = roseList [i].GetComponent<Card> ();
if (RendererExtensions.IsVisibleFrom (card.MyRectTransform, Camera.main))
card.roseImage.enabled = true;
else
card.roseImage.enabled = false;
}
}
}
But now I started too much framerate lose related issue so even scrolling of page become really difficult for me.
Please check the attached video for more clearance about the issue, I was facing.
NameThatRose - Low Frame Rate
Please give me some suggestions for next improvements.
EDIT: Here are my profiler output.
Detailed Deep Profiler
Overview Deep Profiler
You can try few things to find out whats causing the low fps.
Use profiler to deep profile to find out which UI call is taking more time. Like what I did here.
As Image inherits from UIMaskableGraphics, it calls MaskableGraphics.OnEnable() every frame for every image in your list. This takes up time which you can see here:
I believe your OnValueChanged method is called every frame this would only multiply the enable/disable iterations and its processing time. You can limit the call by some time, processing 4 times a second for example.
float timeSinceLastUpdate = 0;
void Update()
{
timeSinceLastUpdate += Time.deltaTime;
}
public void OnValueChangeRefreshCards (Vector2 valueChange)
{
if(timeSinceLastUpdate < 0.25f)
{
return;
}
timeSinceLastUpdate = 0;
// do your stuff here...
}
You have 250+ images to process every frame which is a big deal for older Android devices, again as MaskableGraphics.OnEnable() call can be the culprit. You can avoid changing the state if it is required:
if (RendererExtensions.IsVisibleFrom (card.MyRectTransform, Camera.main))
{
if(!card.roseImage.enalbed)
card.roseImage.enabled = true;
}
else
{
if(card.roseImage.enalbed)
card.roseImage.enabled = false;
}
Furthermore, following are some helpful links to optimize UI in Unity:
Some of the best optimization tips for Unity UI
Tantzy Games
Gemserk
Other UI Optimization Techniques and Tips
A guide to optimizing Unity UI
UPDATE:
The following blog provides more information about UI rendering:
Making the UI Backend Faster
Hope this helps :)
I am adroid proggrammer,because of many object in scene my game has lagging
i have theory for remove lagging in my game.
if i can control rendering in unity i can remove lagging.
using UnityEngine;
using System.Collections;
public class Enemy : MonoBehaviour {
void Update(){
void Start(){
GetComponent<Renderer>().enabled = false;
}
object2 = GameObject.Find("TR");
var distance = Vector3.Distance(gameObject.transform.position, object2.transform.position);
print (distance);
if(distance <= 80){
GetComponent<Renderer>().enabled = true;
}
}
}
Don't work.how can i have boolean render that when have collision will render
else remove.
i want have zone that all object in my zone rendered and allthing outside do not render.
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "zone")
{
GetComponent<Renderer>().enabled = true;
}
else{
GetComponent<Renderer>().enabled = false;
}
don't work
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "zone")
{
gameObject.SetActive(false);
}
else{
gameObject.SetActive(true);
}
This is either implemented in Unity or implementing it is a bad idea because raycasts are expensive and you need a lot of them. Try finding other problems which cause lagging in your game, disable feature by feature and write how many frames you have, this will get you best overview of what's the problem. Look online which methods are expensive (Instantiating, Destroy, try merging all models you have, smaller amount of shaders, fast shaders, less textures to load, FindGameObjectByName (or tag...)).
Here you will find a great document about optimization. It's preapared for mobile devices but i hope you will find what you need: Unity Optimization Guide for x86 Android
I would recommend having your blue blobs in an object pool, and the ones leaving your screen getting disabled.
You know your position and you know the position of the objects in the pool, you can math your distance in one direction, for instance behind you and disable after x amount.
Raycasting or collisions are abundant.
On your terrain generation scripts, check for disabled pool objects and if one exist, it should be put ahead in the level and repositioned or w/e logic you have there.
Don't instantiate and destroy unless you really need it, do it on level-load instead of on the fly.
(It's expensive.)
There's some really good tutorials on the unity page, have a look there.
They cover things like endless-runners.
I am currently trying to move some rectangle objects (displayed as bitmaps on my surfaceview).
They should all move with the same speed, therefor my code looks like this:
new Thread (new Runnable()
{
#Override
public void run()
{
while(true)
{
newTime = System.currentTimeMillis();
frameTime = newTime - currentTime;
currentTime = newTime;
physics(frameTime);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
i move my rectangle objects in the physics method based on the frametime parameter.
So my problem is the following: with the code i just posted all my rectangles move at the same speed, but the graphics are lagging. The lag disappears as soon as i remove the Thread.sleep(1), but then my rectangle objects wont move with the same speed anymore (some rectangles move faster than others).
any ideas?
Edit:
the physics and movedown methods are just normal methods in my classes, both are not in any threads or something. they are only getting called from my Thread i posted above
public void physics(double delta)
{
for(int i=0; i<=5; i++)
{
rectangles[i].moveDown(delta);
}
}
public void moveDown(double delta)
{
setY((double) (getY() + ((sH)*(delta/1000))));
//sH is the screen height
}
Edit2:
Graphics code
while(true)
{
if(!ourHolder.getSurface().isValid())
{
continue;
}
Canvas canvas = ourHolder.lockCanvas();
synchronized(ourHolder) {
graphics(canvas);
// in this method all the drawings happen
// e.g. canvas.drawBitmap
}
ourHolder.unlockCanvasAndPost(canvas);
}
It is difficult to analyse given the code you posted, since it's unclear what physics(frameTime); is actually doing. It seems like each moving rectangle is updated in its own Thread. Then the result you get has to be expected because one thread or the other will be called more often than the others depending on thread scheduling. Instead, use one single thread to control your simulation (e.g. update your frameTime and provide it to the other threads). However, IMHO you have to rethink your architecture.
It's hard to tell from your posted code what the problem could be, but one thing I notice is that you could experience some clock drift. See: Does time jump in android devices?.
Although, it looks like you are moving the rectangles using a fixed offset, so even if there were clock drift, I would expect them to jump around but by the same amount. So I agree with Axel that there appears to be some thread interaction going on.
How are you drawing to the screen? If you have another thread processing the graphics, the two threads could get out of sync and you'll need to ensure you are locking things properly. If you are using the deltas to update the location on the screen in your paint method, maybe instead use an absolute position that you update in your physics() method and draw based on that. It seems you probably need to rethink some of the architecture.
I've got a OnTouchlistener on my ImageView and I've got the feeling like it's blocking the MainThread from time to time. I've got some Animations running, too, and eclipse sometimes tells me that a lot of frames were skipped.
I don't need to track every MotionEvent - it would be enough to call the onTouch-Method every 20-30 miliseconds.
Is there a way to set this?
Also I would like to mention that this onTouchListener is implemented in a special class that is only handling such Touch-Events; which means I could make this class extending Thread. But would this solve the problem if I put a Thread.sleep(ms) at the end of my onTouch-Method?
If that is what you want to accomplish:
add field long lastTime = -1 to your listener class, then in onTouch(MotionEvent event) add:
if(lastTime < 0)
{
lastTime = System.currentTimeMillis();
}
else
{
if(System.currentTimeMillis() - lastTime < 30) //how much time you decide
{
return true; //ignore this event, but still treat it as handled
}
else
{
lastTime = System.currentTimeMillis();
}
}
//your logic
Generally, merely adding a onTouchListener to a ImageView would never cause lag in your UI, unless you are doing a huge amount of calculations per event. If that is the case, I suggest you also look elsewhere for potential problems.
Also, beworker's advice is very good, don't call Thread.Sleep() as it halts everthing else in the thread.
I'm just trying to figure out the best approach for running a scolling background on an android device. The method I have so far.... its pretty laggy. I use threads, which I believe is not the best bet for android platforms
#Override
public void run() {
// Game Loop
while(runningThread){
//Scroll background down
bgY += 1;
try {
this.postInvalidate();
t.sleep(10);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
where postinvalidate in the onDraw function simply pushings the background image down
canvas.drawBitmap(backgroundImage, bgX, bgY, null);
Thanks in advance
UPDATE
I've identified the problem. And it is the fact that my player updates the same rate as the background scrolls (making it look choppy). from top to bottom. This is because both get drawn in the same function. I'm not really sure how to tackle this and would be grateful for any help. i.e so that player movement is handled separately from the map scrolling
Also how can I control the speed at which onDraw(canvas) get called?
Thanks in advance.
However, I have patched together a different run loop for anyone having the same problem. This is partially from the jetboy example on google.
Below is my inner class in my surfaceview
class MapThread extends Thread{
private Map map;
private SurfaceHolder holder;
private boolean run = false;
public MapThread(Map map, SurfaceHolder holder){
this.holder = holder;
this.map = map;
setRunning(true);
}
public void setRunning(boolean run){
this.run = run;
}
#Override
public void run(){
while(run){
try{
Canvas c = null;
try {
c = holder.lockCanvas(null);
synchronized (holder) {
map.onDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
}
}
SOLUTION
https://gamedev.stackexchange.com/questions/8127/android-game-scrolling-background
Use the SurfaceView implementation draw on the screen. It allows you more control of what to draw and when.
The SurfaceView is a special subclass of View that offers a dedicated drawing surface within the View hierarchy. The aim is to offer this drawing surface to an application's secondary thread, so that the application isn't required to wait until the system's View hierarchy is ready to draw.
The basic design is to have a surfaceview that draws continuously in a while loop. Then add an if-statement whose condition is to be true if a timer thread tells you its time to draw. Say, every 30ms, draw the bitmap. This will give you about 33 fps.
Now you may also have another timer thread that tells you when to update the the bgX or bgY values. Say at every 60ms, it will set a boolean updateFlag = true; Then in your main thread, you have an if-statement check this flag, set it to false, and update your bgX and bgY values. By accurately controlling the timer and the bgX/bgY increments, you should be able to produce smooth animations.
It would be a good idea to look at the LunarLander source code provided by Google.
One thing to keep in mind is that sleep is very inaccurate. To work around this, you can keep track of exactly how much time passed during the sleep and update how much you move things accordingly.
Its not clear from you code, but you need to make sure that all of your UI updates happen in the UI thread.
You do need to do your timing outside of the UI thread, because otherwise the UI will never update. There are other methods of timing, like using a Handler that can be a little bit cleaner, but I think the overhead on them might be a bit much for what you are trying to do. I think a simple thread has the least amount of overhead.
I am using this method on the second level of my SpaceQuestAlpha game. This makes a seemless scroll.
I used the 2 lines below to set original position.
moony=0;
moon2y=-(heighty);
Then these lines increment both versions of the background image. One starts at 0 and one starts at negative screen height. Every time one of the images goes below the bottom of the screen it is moved up twice the height to move it back into position. I am using surface view with no latency issues.
moony+=5;
moon2y+=5;
if(moon2y>=heighty) {moon2y=moon2y-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moon2y, null);
if(moony>=heighty){moony=moony-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moony, null);