I implemented input for my mobile game like so:
I have some buttons and I have anywhere else on the screen.
Basically when the player touches the screen and not a button ingame, the character jumps. Now, It is implemented by using OnPointerDown and OnPointerUp that sets a bool:
public class JumpInput : MonoBehaviour
{
public static bool JUMP;
public void PointerDown()
{
JUMP = true;
}
public void PointerUp()
{
JUMP = false;
}
}
If the player is jumping, it is checked by a FixedUpdate method:
if (!movementDisabled && grounded && JumpInput.JUMP)
{
//then we can jump
currentImageIndex = 0;
animState = CharAnimStates.jumpStart;
//added again from here
//zeroing out the Y axis velocity if we had one
rb2d.velocity = new Vector2(rb2d.velocity.x, 0.0f);
//until here
jump = true;
}
As I get decent frame rates even when using post-processing, I have no clue why the input is so delayed. I require assistance to verify if my solution for "anywhere on the screen input" is viable performancewise and it not, how to improve it.
I am developing an Android game using Unity and in my game I am instantiating objects which move in x direction visible to camera and when they leave camera view they are destroyed, to stop slowing the processing and taking memory. It works fine, but when my player moves in vertical direction there are objects which are instantiated but they are out of camera view, and then they are not destroyed. Therefore, when my objects are out of camera view I want to stop invokeRepeating. Here is the code which works only when objects are in view.
public GameObject colorbar;
public Vector2 velocity = new Vector2(-3, 0);
void Start () {
GetComponent<Rigidbody2D>().velocity = velocity;
transform.position = new Vector3(transform.position.x, transform.position.y, 0);
InvokeRepeating("CreateObstacle", 1f, 1.8f);
}
// Update is called once per frame
void Update () {
}
public void OnBecameInvisible()
{
Destroy(colorbar);
Debug.Log ("Color bar is Destroyed");
}
void CreateObstacle()
{
Instantiate(colorbar);
Debug.Log ("Color bar instantiating");
}
}
You can check if the renderer is visible when you create the object:
http://docs.unity3d.com/ScriptReference/Renderer-isVisible.html
void Awake(){
if(GetComponent<Renderer>().isVisible == false){
Destroy(this.gameObject);
}
}
But best would be to avoid creation if not needed, so you could get the point where you object should be created, then convert to screen point:
http://docs.unity3d.com/ScriptReference/Camera.WorldToScreenPoint.html
if the value is within the boundaries of the screen, then you good. You could also linecast from position to camera and if there is nothing stopping the cast, you are double good to create.
Vector3 pos = ObjectPos;
Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
if(screenPos.x > 0 && screenPos.x < Camera.main.pixelWidth &&
screenPos.y > 0 && screenPos.y < Camera.main.pixelHeight && screenPos.z > 0)
{
if (Physics.Linecast(pos, Camera.main.transform.position) == false){
// Create object
}
}
Keep in mind this is using the position of the object. If your object is large, then the position may be outside while some edges would be inside. The best would be to check for each vertex of the mesh. But if you have a complex mesh, that might get expensive, if you deal with a cube then maybe.
I am making a game in which my player is a UFO, and when the player collides with other game objects, I need the game objects to be attached or floated in the air below the player (UFO), like original UFO. I tried to attach them as a child, but it didn't worked.
I made one script as below:
if (coll) {
distance = Vector2.Distance (this.transform.position, player.transform.position);
if (distance < 2) {
this.transform.parent = encaixe.transform;
this.transform.localPosition = new Vector2 (0f, 1.2f);
this.transform.localRotation = Quaternion.identity;
encaixe.rigidbody2D.gravityScale=0;
}
}
In using this script, the gameobject is attaching, but the player is not moving as like original. The game object is pulling down or up forcefully.
Please suggest to me how to do this.
I know that this Thread is rather old, but maybe someone might come across this question and is in need of an answer.
You can actively set the Object's Position and Rotation to the UFO's on Collision.
Something like the following (pseudocode):
private bool hasCollided = false;
void OnCollisionEnter()
{
hasCollided = true;
}
void LateUpdate()
{
if (hasCollided)
{
followPlayer();
}
}
void followPlayer()
{
//update position and rotation
}
I have an application using a GlSurfaceView and renderer. I have it set so that when the user exits the application via the back button I call myActivity.finish();
This is fine and I can see the activity getting calls to onStop() and onDestroy();
The app works fine the first time run however when I subsequently run I have had a problem with my motionEvents.
I handle motion events by queuing them into a pool and having the renderer access the pool at the right time like so:
try
{
//Get the history first
int hist = event.getHistorySize();
if (hist > 0)
{
//Oldest is first in the list. (I think).
for (int i=0;i <hist; i++)
{
InputObject input = inputObjectPool.take();
input.useEventHistory(event, i);
defRenderer.feedInput(input);
}
}
//The current one still needs to be added
InputObject input = inputObjectPool.take();
input.useMotionEvent(event);
defRenderer.feedInput(input);
}
And in the renderer:
synchronized (inputQueueMutex)
{
ArrayBlockingQueue<InputObject> inputQueue = this.inputQueue;
while (!inputQueue.isEmpty()){try
{
InputObject input = inputQueue.take();
if (input.eventType == InputObject.EVENT_TYPE_TOUCH)
{
screenManager.processMotionEvent(input);
}
else if (input.eventType == InputObject.EVENT_TYPE_KEY)
{
screenManager.processKeyPress(input);
}
input.returnToPool();
}
catch (InterruptedException ie)
{
DLog.defError("Interrupted blocking on input queue.", ie);
}
}
}
As you can see in the above code I hand these motion events to the ScreenManager which basically is my way of having several "scenes" which I render out. This works fine the first time I run the application and the screen interprets my motion touches into movement of a simple square at the moment.
However the second time I run the application the square is drawn to the screen fine however the motion events do nothing.
I have followed the motion events and although they are given to the "new" renderer it seems to be giving the motion events to an old screen. Or rather to an old object on the screen. This is confusing as in my code in the onCreate() method I do this:
//Set up the renderer and give it to the SurfaceView
defRenderer = new DefRenderer();
defView = new DefView(this);
defView.setRenderer(defRenderer);
//Set out content to the surface view.
setContentView(defView);
//Set up the input queue
createInputObjectPool();
OnCreate is called both the first time and the second time my app is run (and the app was destroyed!) the screens are made new in defRenderer and are given to a new defView.
I am very confused how data could remain in the defRenderer to receive the motionEvents as the app is completely remade.
Is there something obvious going on that I am missing here? I would have thought that when onDestroy is called the app would be completely dereferenced and so no trace of it would remain. Is this not true? Does somehow when I call new Renderer(); is it referencing an old one?
I am at a loss as to what is going on really. Especially as this app is a basic copy of another I have written which works completely fine!
EDIT:
After a small amount of experimentation I have discovered that the motion events are actually going to an old ScrollPanel (an object I made..) which is registered as a listener (and by listener I mean my own implementation ..) for MotionEvents. I have made my own event system for these like so:
public interface TouchSource
public static final int TYPE_TOUCHDOWN = 0;
public static final int TYPE_TOUCHDRAG = 1;
public static final int TYPE_TOUCHCLICK = 2;
public Vector<TouchListener> listeners = new Vector<TouchListener>();
public void addTouchListener(TouchListener listener);
public void removeTouchListener(TouchListener listener);
public void touchOccured(int type, int xPos, int yPos);
}
And the listener interface:
public interface TouchListener
public boolean touchDownOccured(int xPos, int yPos);
public boolean touchDragOccured(int xPos, int yPos);
public boolean touchClickOccured(int xPos, int yPos);
So the Screen implements touchSource and so has a list of the listeners. Now despite being REMADE by Screen currentScreen = new Screen(); called in the OnCreate(); of the manager this list of listeners is still populated with the old ScrollPanel?
How is this? I'm clearly missing something obvious. Like somehow the list of listeners is static for some reason and not getting dereferenced despite the app being completely remade?
I suspect the issue you're facing might have something to do with the fact that the original motionevents are recycled (returned to their pool) by the framework after the onMotionEvent() returns.
From the way you're using your InputObjects, I think you might be keeping a reference to the original motionevents in there, and not copying the event data.
Quickly try using MotionEvent.obtain(event) whereever you use event now (this makes a copy) and see if this makes the weird behaviour go away. Naturally, if this works you will eventually have to recycle() those copies after you're done with them. Do not call recycle() on the original motionevents though.
Cheers, Aert.
I based my game off of the lunar lander demo, although heavily modified, and I can get around 40-50fps but the problem is it fluctuates between 40-50fps so much that it causes the moving graphics to jitter! Its very annoying and makes my game look really shitty when in fact its running at a good frame rate.
I tried setting the thread priority higher but that just made it worse... now it will fluctuate between 40-60fps...
I was thinking of limiting the FPS to about 30 so that it will be constant. Is this a good idea and does anyone else have experience or a different solution?
Thanks!
This is my run loop
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mMode == STATE_RUNNING){
updatePhysics();
}
doDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
private void updatePhysics() {
now = android.os.SystemClock.uptimeMillis();
elapsed = (now - mLastTime) / 1000.0;
posistionY += elapsed * speed;
mLastTime = now;
}
Don't base your game's logic (object movement, etc.) updating rate on the framerate. In other words, put your drawing and logic updating code in two separate components/threads. This way your game logic is completely independent from your framerate.
Logic updating should be based on how much time has passed since the last update (let's call it delta). Therefore, if you have an object moving at 1px/millisecond, then during each update your object should do something like this:
public void update(int delta) {
this.x += this.speed * delta;
}
So now even if your FPS lags, it won't affect your object's movement speed, since the delta will just be larger, making the object move farther to compensate (there are complications in some cases, but that's the gist of it).
And this is one way of calculating delta within your logic updating object (running in some thread loop):
private long lastUpdateTime;
private long currentTime;
public void update() {
currentTime = System.currentTimeMillis();
int delta = (int) (currentTime - lastUpdateTime);
lastUpdateTime = currentTime;
myGameObject.update(delta); // This would call something like the update method above.
}
Hope that helps! Please ask if you have any other questions; I've been making Android games myself. :)
Sample code:
Copy these two snippets (1 activity and 1 view) and run the code. The result should be a white dot smoothly falling down your screen, no matter what your FPS is. The code looks kinda complicated and long, but it's actually quite simple; the comments should explain everything.
This activity class isn't too important. You can ignore most of the code in it.
public class TestActivity extends Activity {
private TestView view;
public void onCreate(Bundle savedInstanceState) {
// These lines just add the view we're using.
super.onCreate(savedInstanceState);
setContentView(R.layout.randomimage);
RelativeLayout rl = (RelativeLayout) findViewById(R.id.relative_layout);
view = new TestView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
10000, 10000);
rl.addView(view, params);
// This starts our view's logic thread
view.startMyLogicThread();
}
public void onPause() {
super.onPause();
// When our activity pauses, we want our view to stop updating its logic.
// This prevents your application from running in the background, which eats up the battery.
view.setActive(false);
}
}
This class is where the exciting stuff is!
public class TestView extends View {
// Of course, this stuff should be in its own object, but just for this example..
private float position; // Where our dot is
private float velocity; // How fast the dot's moving
private Paint p; // Used during onDraw()
private boolean active; // If our logic is still active
public TestView(Context context) {
super(context);
// Set some initial arbitrary values
position = 10f;
velocity = .05f;
p = new Paint();
p.setColor(Color.WHITE);
active = true;
}
// We draw everything here. This is by default in its own thread (the UI thread).
// Let's just call this thread THREAD_A.
public void onDraw(Canvas c) {
c.drawCircle(150, position, 1, p);
}
// This just updates our position based on a delta that's given.
public void update(int delta) {
position += delta * velocity;
postInvalidate(); // Tells our view to redraw itself, since our position changed.
}
// The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
new Thread() {
public void run() {
// Store the current time values.
long time1 = System.currentTimeMillis();
long time2;
// Once active is false, this loop (and thread) terminates.
while (active) {
try {
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
time2 = System.currentTimeMillis(); // Get current time
int delta = (int) (time2 - time1); // Calculate how long it's been since last update
update(delta); // Call update with our delta
time1 = time2; // Update our time variables.
}
}
}.start(); // Start THREAD_B
}
// Method that's called by the activity
public void setActive(boolean active) {
this.active = active;
}
}
I am thinking there might be, not really something wrong with some of the above code, but rather an inefficiency. I am talking about this code...
// The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
new Thread() {
public void run() {
// Store the current time values.
long time1 = System.currentTimeMillis();
long time2;
// Once active is false, this loop (and thread) terminates.
while (active) {
try {
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
time2 = System.currentTimeMillis(); // Get current time
int delta = (int) (time2 - time1); // Calculate how long it's been since last update
update(delta); // Call update with our delta
time1 = time2; // Update our time variables.
}
}
}.start(); // Start THREAD_B
}
Specifically, I am thinking about the following lines...
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
It seems to me that just having the thread hang out doing nothing is a waste of valuable processing time, when in fact what you want to be doing is performing the updates, then, if the updates have taken less time than the 25 millis, then sleep the thread for the difference of what was used during the update and 25 millis (or whatever your chosen frame rate is). In this way the update will happen while the current frame is being rendered, and will be completed so the next frame update uses the updated values.
The only problem I can think of here is that some kind of syncronization will need to occur so that the current frame render does not use partially updated values. Perhaps update into a new instance of your set of values, and then make the new instance the current instance just before rendering.
I think I remember reading something in a graphics book about the goal being to perform as many updates as you can while staying within your desired frame rate, then, and only them, perform a screen update.
This of course will require one thread to drive the updates - if you use a SurfaceView, the render is controlled by this thread when you lock the canvas (in theory, according to my understanding anyway).
So, in code, it would be more like...
// Calculate next render time
nextRender = System.currentTimeInMillis() + 25;
while (System.currentTimeInMillis() < nextRender)
{
// All objects must be updated here
update();
// I could see maintaining a pointer to the next object to be updated,
// such that you update as many objects as you can before the next render, and
// then continue the update from where you left off in the next render...
}
// Perform a render (if using a surface view)
c = lockCanvas() blah, blah...
// Paint and unlock
// If using a standard view
postInvalidate();
Good luck and any feedback from anyone using this would surely help us all learn something...
rpbarbati
I think it's about Garbage collector
I would use SurfaceView instead of View if your game is action heavy. If you don't need to update the GUI rapidly then View is fine but for 2D games it's always better to use SurfaceView.
I have a similar issue, the jitter makes large object moves look uneven. Even though the "speed" is the same, different lengths of steps make the movements look jumpy.
Broody - You say a SurfaceView is beter, however, this is not true after Android 3.0 as the View is HW accelerated but the canvas returned by .lockCanvas is not.
Steven - Yes, this is likely causing poroblems, but is easy to detect.
/Jacob