Camera rotation not rotating correctly on mobile - android

I'm making a FPS and I want the player to rotate the camera, my code works for PC, but on mobile if I'm rotating the camera and I also touching the fire button (or anywhere else with my other finger) the camera rotates to right (here where my fire button is) and I don't know if I can do something about it, or I need to cancel the release for android and IOS and publish my game only for PC
Part of my code:
if (CanProcessInput())
{
// Check if this look input is coming from the mouse
bool isGamepad = Input.GetAxis(stickInputName) != 0f;
float i = isGamepad ? Input.GetAxis(stickInputName) : Input.GetAxisRaw(mouseInputName);
// handle inverting vertical input
if (InvertYAxis)
i *= -1f;
// apply sensitivity multiplier
i *= LookSensitivity;
if (isGamepad)
{
// since mouse input is already deltaTime-dependant, only scale input with frame time if it's coming from sticks
i *= Time.deltaTime;
}
else
{
// reduce mouse input amount to be equivalent to stick movement
i *= 0.01f;
#if UNITY_WEBGL
// Mouse tends to be even more sensitive in WebGL due to mouse acceleration, so reduce it even more
i *= WebglLookSensitivityMultiplier;
#endif
}
return i;
}

Segment the touch input so you ignore values that are generated in the area of the fire button. You can do this by checking the Touch. position value:
Description
The position of the touch in screen space pixel coordinates.
Position returns the current position of a touch contact as it's dragged. If you need the original position of the touch see Touch.rawPosition.
The documentation hints at what you might be interested in, too - the original touch position. Your question body is asking about horizontal motion, but your code is referencing y values. The position coordinates are given in (width, height), so I just wanted to be clear up front that I'll use x values to answer your question about horizontal motion.
So if you know that the bottom-left corner of the screen is (0,0) and you can use Screen.width to get the width in pixels, then you could calculate the right 20% of the screen as reserved for the fire button. Anything less than that would be acceptable inputs for rotation, but the 80%-of-width pixel would be the maximum allowable input:
private int maxAllowablePixel;
void Start()
{
maxAllowablePixel = 0.8f * Screen.width;
}
and then only process the touch as a rotation if the original touch was less than that value:
if(touch.rawPosition.x < maxAllowablePixel)
{
DoRotation(touch.position);
}
and again here you are allowing the user to put their finger down in the middle of the screen, drag it over the fire button, and still rotate that much, but any touches that originate in the fire button "exclusion zone" are ignored for the purposes of doing rotations.
Also when you do the rotation, do it where the finger is now (Touch.position) and not where it started (Touch.rawPosition).

The way I always solve an issue like this is to not use the touch positions directly, but to have a "TouchReceiver" Object in the Canvas with a script that implements IDragHandler. So the class might look like this:
[RequireComponent(typeof(Image))]
public class TouchReceiver : MonoBehaviour, IDragHandler
{
public void OnDrag(PointerEventData data)
{
Vector2 delta = data.delta;
// do something with delta, like rotating the camera.
}
}
Then set up your canvas like so:
Make sure to put the "TouchReceiver" behind the fire button, that way the OnDrag event will not occur when pressing the fire button, because the fire button blocks the "TouchReceiver" object itself. The "TouchReceiver" object needs to have an Image component, otherwise it won't be able to receive any touches. Of course the image should then be made totally transparent.
With this setup is pretty easy to move the fire button around without changing anything else. This might also come in handy when adapting to different screen resolutions.

Related

Dragging a sprite in Android

I'm simply trying to drag a sprite around using my finger. I'm doing this by detecting the distance the finger that is touching the screen has moved by and then moving the sprite by the same amount.
This is an openGL ES 2.0 app, so my rendering and logic updating takes place on my GL Rendering thread, and obviously touch events are captured on the UI thread.
So, my setup is something like this:
UI Thread
case MotionEvent.ACTION_MOVE: {
movedByX = event.getX()-oldXPosition;
movedByY = event.getY()-oldYPosition;
oldXPosition = event.getX();
oldYPosition = event.getY();
break;
}
GL Rendering thread
Rendering
#Override
public void render() {
drawSprite(testSprite); //Draws using the sprites' internal x and y coordinates
}
logic update
#Override
public void updateLogic() {
testSprite.x+=movedByX; //Update the sprite's X position
testSprite.y+=movedByY; //Update the sprite's Y position
}
The issue
If one drags the sprite around the screen for a while, and then stops. The resting point of the finger relative to the sprite is not the same as it was when the finger initially went down. Take for a example a circlar sprite like so. The blue circle is the sprite and the red dot represents the finger/pointer.
So as you can see, it doesn't quite work as expected and I'm not sure why.
I had a similar question in previously in which I 'worked around' the problem by initially grabbing the X and Y in ACTION_MOVE (onTouchEvent/UI Thread) as I am above, but then in my updateLogic method, I make a copy of it and work out the 'moveByY' amount there before applying it to my sprite's position.
Doing this effectively solved the problem of the finger 'wandering' - but - it makes the movement of the sprite very choppy, therefore I can't use this solution.
I think this choppiness may be because the rendering thread sometimes runs twice without the UI thread running, therefore, even though the finger has moved, the logic is still using the version it has because onTouch hasn't been able to capture to actual most up to date finger position. But I'm not 100% sure.
If I simply update my sprite's positionin my UI thread, (again, in ACTION_MOVE) - again, I get very choppy movement but the pointer position does remain correct).
So, I need to keep the smooth movement that I get from the method outlined at the top of the question, but I need to know why the moveBy amount is causing the sprite to wander from the finger.
Other Notes
I need to move the sprite using a the difference between the finger's old and current positions, and not simply draw the sprite at the finger's current position because this will eventually become part of a 'scrollable' menu system.
All of my variables are declared as 'private float volatile' and my onTouchEvent and updateLogic methods are synchronised.
i dont know if its a typo
#Override
public void updateLogic() {
testSprite.x+=movedByX; //Update the sprite's X position
testSprite.x+=movedByY; //Update the sprite's Y position
}
but you set testsprite.x both times

How to only detect a swipe if its longer than 100 px LibGDX

On my game screen i want to have a swipe detected only if its more than 100px, this is because the user taps a lot on the game screen and it tends to detect a swipe which changes the screen back to title. How can i make the swipe detect only if its longer than 100px?
There are two ways to achieve this.
The first one is to save the starting point of the touch and measure the distance on end of the touch event, just like Paul mentioned.
The second is to enlarge the tap square size if you use the GestureDetector of libgdx. Its defaulted to 40px which means if you're finger moves more than 20px to any side it's no longer a tap event but a pan event. I'd recommend using the GestureListener/Detector as it will give you the basic mobile gestures out of the box rather than recoding them.
On a side note: Determining the distance by pixels is error-prone because the pixel density will vary between mobile devices, especially if you code for android! 100px on one device may be only half the distance than on another device. Take pixel density into consideration when doing this or change to relative measurements like 1/3 of the screen size!
Save the position in the touch up and down.
private Vector2 downPos = new Vector2(), upPos = new Vector2();
private Vector3 tmp = new Vector3();
public void touchDown(float x, float y.....) {
tmp.set(x, y, 0);
camera.unproject(tmp);
downPos.set(tmp.x, tmp.y);
}
public void touchUp(float x, float y.....) {
tmp.set(x, y, 0);
camera.unproject(tmp);
upPos.set(tmp.x, tmp.y);
float distance = downPos.dst(upPos); // the distance between thoose vectors
if (distance > 100) {
// there was a swipe of a distance longer than 100 pixels.
}
}
If you don't want to do that only on touch up, put the code in the touchdrag method.

Best approach to make a background scrolling game?

i want to do a 2D game with backgrounds and sprites (views) moving on the screen.
I want to make a game with a scrolling ground. I mean the user must see a horizon in the top part of the screen filling the 30% of the screen size. The ground must be scrolling and must be the 70% of the screen size. For example, if i put a car on the ground, the car must be driving into a scrolling road and the sky (horizon) must be seen on the screen, in the top of the road, filling the 30% of the screen.
I am searching in google about scrolling games but i can't find the way to achieve this kind of scrolling ground game with horizon.
Any ideas and approaches will be grated, i'm just making a research about how to do this.
Thanks
This kind of effect can be done in various ways, here is one very basic example I can come up with.
First create a background image for your horizon - a blue sky with a sun would be good. Now create some detail images for the background, such as clouds and birds. These can move accross the background image from left to right (and/or vice-versa). In your rendering code you would render the "background" image first, and then the "detail" images. Make sure that your background image covers around 35% of the screen, so that when you render the 70% ground layer there is some overlap - preventing a hole where the two layers meet.
Next create a textured image for the ground. For this I would use a static image that has the correct type of texture for what you are trying to represent (such as dirt). It may also be good to add some basic detail to the top of this image (such as mountains, trees, etc).
This should be rendered after the background layer.
Once you have this layout in place, the next step would be to simulate the depth of your world. For this you would need to create objects (2D images) that would be placed in your "world". Some examples would be trees, rocks, houses, etc.
To define your world you would need to store 2 coordinates for each object - a position on the x-axis as well as a depth value on the z-axis (you could also use a y-axis component to include height, but I will omit that for this example).
You will also need to track your player's position on the same x and z axis. These values will change in realtime as the player moves into the screen - z will change based on speed, and x will change based on steering (for example).
Also define a view distance - the number of units away from the player at which objects will be visible.
Now once you have your world set up this way, the rendering is what will give the illusion of moving into the screen. First render your player object at the bottom of the ground layer. Next, for each world object, calculate it's distance to the player - if it's distance is within the view distance you defined then it should be rendered, otherwise it can be ignored.
Once you find an object that should be rendered, you need to scale it based on it's distance from the player. The formula for this scaling would be something like:
distance_from_player_z = object.z - player.z
scale = ( view_distance - distance_from_player_z ) / view_distance
This will result in a float value between 0.0 and 1.0, which can be used to scale your object's size. Using this, the larger the distance from the player, the smaller the object becomes.
Next you need to calculate the position on the x-axis and y-axis to render your object. This can be achieved with the simple 3D projection formulas:
distance_from_player_x = object.x - player.x
x_render = player.x + ( distance_from_player_x / distance_from_player_z )
y_render = ( distance_from_player_z / view_distance ) * ( height_of_background_img );
This calculates the distance of the object relative to the player on the x-axis only. It then takes this value and "projects" it, based on the distance it is away from the player on the z-axis. The result is that the farther away the object on the z-axis, the closer it is to the player on the x-axis. The y-axis part uses the distance away from the player to place the object "higher" on the background image.
So with all this information, here is a (very basic) example in code (for a single object):
// define the render size of background (resolution specific)
public final static float RENDER_SIZE_Y = 720.0f * 0.7f; // 70% of 720p
// define your view distance (in world units)
public final static float VIEW_DISTANCE = 10.0f;
// calculate the distance between the object and the player (x + z axis)
float distanceX = object.x - player.x;
float distanceZ = object.z - player.z;
// check if object is visible - i.e. within view distance and in front of player
if ( distanceZ > 0 && distanceZ <= VIEW_DISTANCE ) {
// object is in view, render it
float scale = ( VIEW_DISTANCE - distanceZ ) / VIEW_DISTANCE;
float renderSize = ( object.size * scale );
// calculate the projected x,y values to render at
float renderX = player.x + ( distanceX / distanceZ );
float renderY = ( distanceZ / VIEW_DISTANCE ) * RENDER_SIZE_Y;
// now render the object scaled to "renderSize" at (renderX, renderY)
}
Note that if distance is smaller than or equal to zero, it means that the object is behind the player, and also not visible. This is important as distanceZ==0 will cause an error, so be sure to exclude it. You may also need to tweak the renderX value, depending on resolution, but I will leave that up to you.
While this is not at all a complete implementation, it should get you going in the right direction.
I hope this makes sense to you, and if not, feel free to ask :)
Well, you can use libgdx (http://libgdx.badlogicgames.com/).
The superjumper example will put you in the right way :) (https://github.com/libgdx/libgdx/tree/master/demos/superjumper)

Android - Rotating background image by fixed screen position

I am attempting to create simple top down side scrolling style app in android. Basically an image sits in the bottom center of the screen which does not move. A background image behind this will move based on the tilt of the device.
If I don't move (translate) the image and I rotate it based upon the first images center point, the background image rotates correctly around this point. If I translate the background image at all however, the rotation is no longer accurate. It seems that the rotation is applied based on the images Top, Left not the screen top left, so if I translate the image by 100 units down the screen, it now rotates 100 units below where I want it to.
Basically I am looking for a way to rotate an image no matter where it is located on the screen by a fixed position on the screen that never changes. Hopefully this will create the illusion that the image at the bottom of the screen is moving when in fact it is the background that is moving. Thanks.
Here's some simple code snippets of what Im doing, not sure if it really helps:
m_X & m_Y are variables holding the tilt of the device
That horrible hard coded value of 264, 628 is the location of my animating character at the bottom of the screen. (what I want the background image to rotate around no matter where it has translated itself to)
Update Function:
protected void Update(float a_GameTime)
{
super.Update(a_GameTime);
m_CharacterAnimator.Update(a_GameTime);
if ((m_CurrentCharacterDirection.contains(LEFT) || m_CurrentCharacterDirection.contains(RIGHT)) && (m_X > 1.0f || m_X < -1.0f))
{
m_BackgroundWorldMatrix.preRotate(m_X * .05f, 264, 628);
}
if ((m_CurrentCharacterDirection.contains(FORWARD) || m_CurrentCharacterDirection.contains(BACKWARD)) && (m_Y > 1.0f || m_Y < -1.0f))
{
m_BackgroundWorldMatrix.postTranslate(0.0f, m_Y * -0.25f);
}
}
Draw Function:
protected void Draw(Canvas a_Canvas)
{
super.Draw(a_Canvas);
a_Canvas.drawBitmap(m_Background, m_BackgroundWorldMatrix, null);//, m_BackgroundWorldMatrix, null);
// Draw the animating Character
m_CharacterAnimator.Draw(a_Canvas);
}

Problems with touch event

Speaking of events we know that the touch screen method onTouchEvent (MotionEvent events) allows you to capture touch events on the screen and event.getX () and
event.getY () give me the coordinates of the touch as float values ​​(ie values ​​with a comma).
Specifically, I realized that taking logcat using the fixed point the finger in a mobile phone screen and not moving it, the event is not only perceived but MotionEvent.ACTION_DOWN MotionEvent.ACTION_MOVE and returned coordinates are manifold. This i can understand because the surface of the finger touches more points and more by reading the device coordinates believe that the finger moves while holding it.
My problem is that I would read the color of one pixel of a color image when I hold your finger still. In particular I need the central area occupied by the finger.
First, assuming you have a Bitmap object, I have to round to integer coordinates of the touch as the getPixel (intX, int y) coordinates of the entire Bitmap wants to return the pixel color.
Then how do I get the central point of touch? Be that there is a function that can help me? Keep in mind that while I stopped and read the value of a pixel on the image then I start to move slowly and even though I always record every move the center pixel. I need this because every move, if you change the color of the image in pixels, I want to vibrate or not the device.
If I could stop thinking about themselves in a dynamic array to store the coordinates of different pixels and search for the center of gravity, but moving I do not know what to do. I think however, that the research center of gravity, if not done very efficiently, can be slow during the move and then give the wrong results. If the CG is not quite enough for me to another point belonging to the environment.
I hope you can help me maybe with a few lines of code sketched.
Thanks in advance.
To get the touch position of finger, just cast the getX() and getY() to int and perform your desired tasks.
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN ) {
getPixelAt((int)event.getX(), (int)event.getY());
}
}
you can put you on main activity class when you can want to touch event occurs. let see you can put following code or not ? and return is most important as well.
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mDistanceX = 0;
}
return super.onTouchEvent(event);
}

Categories

Resources