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);
}
Related
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.
My programs detects collision between the image and the edge of the screen, but I cannot drag the image after collision. What do I change so I could still be able to drag the image around and didn't let the image go outside of the screen edges?
Gif example I made: https://s31.postimg.org/f60hh3z2z/Animation.gif
inside render
batch.begin();
batch.draw(texture, spritePosition.x, spritePosition.y);
batch.end();
if (Gdx.input.isTouched() && isNotColliding()) {
camera.unproject(spritePosition.set(Gdx.input.getX() - texture.getWidth() / 2, Gdx.input.getY() + texture.getHeight() / 2, 0));
}
textureBounds.set(spritePosition.x, spritePosition.y, texture.getWidth(), texture.getHeight());
.
private boolean isNotColliding(){
return screenBounds.contains(textureBounds);
}
The position of the sprite is the lower left corner. This position is what you use to check if the sprite is colliding with the edge.
Because you can move the sprite more than 1 pixel in a frame you can have the sprite x position to be negative. If spritePosition.x == -1 you will be able to see the sprite just fine, but the first line of pixels will be outside the screen.
The reason you are not able to move the sprite anymore is becouse of your if
(Gdx.input.isTouched() && isNotColliding()) {
When spritePosition.x is < 0 isNotColliding() will be true.
Insead of not alowing the sprite to be moved, make it stay inside the screen.
if(spritePosition.x < 0){
spritePosition.x = 0;
}
I have a huge Quad and a PNG as a child object.
The PNG covers the whole game area / screen.
I know how to detect taps, but how can I detect when a user tapped on the left side of the PNG/quad vs. the right?
Should I place 2 gameobjects above the big quad to capture inputs on right or left? Or maybe I could capture the tap on the main big quad but I do a calculation to find out where it as tapped, like this:
if (tappedPosiion > quadWidth / 2)
{
// it's the right side
} else {
// its the left
}
The problem is I don't know how do I find out the width of the current game object!
Renderer.bounds.size should work (or collider.bounds.size)
http://answers.unity3d.com/questions/24012/find-size-of-gameobject.html
You can then access the width by getting the x value:
"size.x is the width, size.y is the height and size.z is the depth of the box."
http://docs.unity3d.com/ScriptReference/Bounds-size.html
Now you have the width, so it should be relatively easy to find out where they tapped.
if(tappedLocation == width/2)
//middle
if(tappedLocation > width/2)
//right side
if(tappedLocation < width/2)
//left
pretty basic but answers your question on how to find width and see where they tapped based on that information.
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.
I am experimenting with 2D graphics in Android during my off time, and I'm intrigued by the idea of creating a sprite based "sandbox" for playing around. I've looked around online and found some good tutorials, but I'm stuck with a fairly basic problem: My sprite moves faster than the terrain and "glides" over it. Also, he slowly outpaces the terrain scrolling and moves to the edge of the view.
I've got an animated sprite that I want to move around the world. I do so by changing his absolute coordinates(setting X and Y position in an 'update()' function and applying a scalar multiple to speed up or slow down the rate at which he's moving around.
this.setXPos(currX + (speed * dx2));
this.setYPos(currY + (speed * dy2));
Underneath that sprite, I'm drawing "terrain" from a bitmap. I want to continue to move that sprite around via the coordinate accessors above, but also move my view and scroll the terrain so that the sprite stays in the same relative screen location but moves through the "world". I have an imperfect implementation where player is my sprite and field is my terrain:
#Override
protected void onDraw(Canvas canvas)
{
player.updateLocation(GameValues.speedScale);
field.update(new Point(player.getXPos(), player.getYPos()));
field.draw(canvas);
player.draw(canvas);
//more stuff here...
}
And field.update() looks like(Warning: Hard-coded scariness):
public void update(Point pt)
{
sourceRect.left = pt.x - 240;
sourceRect.right = pt.x + 240;
sourceRect.top = pt.y - 400;
sourceRect.bottom = pt.y + 400;
}
The thinking there was that I would eventually just get screen dimensions and make it less 'hack-y', but get something together quickly. This could easily be where my issue is coming from. Of immediate interest is field.draw():
#Override
public void draw(Canvas canvas)
{
try
{
canvas.drawBitmap(fieldSheet, sourceRect, destRect, null);
}
catch(Exception e)
{
//Handle Exception...
}
}
You'll notice I'm using the overload of drawBitmap() that accepts a source and destination Rect and that the field.update() method moves that sourceRect to match the movement of the sprite. How can I keep the "camera (so to speak)" centered on the sprite and scroll the terrain appropriately? I had thought that moving just the sourceRect and maintaining a constant destRect would do so, but now I'm thinking I have to move the destRect around the "world" with the sprite, while maintaining the same dimensions.
Is there a better (read functional) way of making this work? I'm coming here somewhat shamefully, since I think this should be somewhat easier than it seems to be for me. Any and all help or suggestions are appreciated. Thanks!
*Edit:*Does it make sense to also move the destination Rect at the same speed as the sprite? I think I conflated the source and destination Rect's and left the destRect immobile. I'll update with the results after I get a chance to try it (maybe during lunch).
*Edit_2:*Changing the destination rectangle didn't get me there, but using the View.scrollBy(x, y) method gets me close to totally satisfied. The remaining question to be satisfied is how to "clip" the View scrolling to a rectangle that represents the "field". I believe that the View.getLeft() and View.getTop() functions, offset by the screen width and height, can be used to specify a Rect that can be virtually moved around within the constraints of the "world" and block further deltas from being argued to the View.scrollBy() method. The reason I look toward this approach is because the View doesn't seem to be positioned in absolute space, and a View.getLeft() call, even after a View.scrollBy(x, y) where x > 0, returns a 0.