Using TouchScreens for game control - iphone

I'm working on my first video game for the Android platform as a bit of a nights and weekends project.
It is coming along nicely, but I am very unhappy with the control sensativity.
In this game, you move an object left and right on the screen. On the bottom of the screen is a "touchpad" of sorts, which is where your finger should rest.
/-------------------------\
| |
| |
| |
| Game Area |
| |
| |
| |
| |
| |
/-------------------------\
| |
| Touch Area |
| |
\-------------------------/
I am currently using a state variable to hold "MOVING_LEFT, MOVING_RIGHT, NOT_MOVING" and am updating the location of the player object each frame based on that variable.
However, my code that reads the touchscreen input and sets this state variable is either too sensative, or too laggy, depending on how I tweak it:
public void doTouch (MotionEvent e) {
int action = e.getAction();
if (action == MotionEvent.ACTION_DOWN) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
}
else if (action == MotionEvent.ACTION_MOVE) {
if ((int)e.getX() >= this.mTouchX) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
if (this.TouchRect.contains(this.mTouchX, this.mTouchY)) {
this.mTouchDirection = MOVING_RIGHT;
}
}
else if ((int)e.getX() <= this.mTouchX) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
if (this.TouchRect.contains(this.mTouchX, this.mTouchY)) {
this.mTouchDirection = MOVING_LEFT;
}
}
else {
this.mTouchDirection = NOT_MOVING;
}
}
else if (action == MotionEvent.ACTION_UP) {
this.mTouchDirection = NOT_MOVING;
}
}
The idea is that when there is any movement, I check the previous location of the users finger and then figure out what direction to move the player.
This doesn't work very well, I figure there are some IPhone/Android developers on here who have figured out how to do good controls via a touchscreen and can give some advice.

You could try something similar to "drag rectangles" on Windows. When you hold down the mouse button on something, you don't start a drag operation until the mouse moves outside a small region around the mouse-down location. Reason being that it's very hard to keep the cursor on the same pixel while clicking.
So a first attempt could be (int)e.getX() >= this.mTouchX + DEAD_ZONE and similar for the other case, where DEAD_ZONE is a small integer.
This does not deal with turning around in the same stroke, however. You could deal with that by only turning left when the current position is at least DEAD_ZONE pixels to the left of the last position after turning right, and vice versa.

One obvious problem is that nowhere do you take account of the time since the last touch.
I would suggest you treat the players touch as an expression of desired movement on an analogue range from -x/+x and -y/+y, then perform the actual movement elsewhere based on time.
E.g.
objectPos += objectPos + (joyPos * timeDelta * maxSpeed);
So if the max-speed of your object is 10ms-1 and the players finger is at 0.5 on the control pad then the object would be moving 5 meters every second. If your game is running at 10fps then each frame the object would move 0.5 meters.
These figures are fictional, but hopefully demonstrate the need to separate control from movement and then factor in time.

Related

When releasing two buttons in the new Input System it detects the differences

I've been creating a game that uses the input system for moving the player
_playerInput.actions["Move"].performed += x =>
{
// We're moving
_walking = true;
// Set the movement input to the value passed by the player
_movementInput = x.ReadValue<Vector2>();
// The angle that we need the player to rotate towards so the player look where he's moving and we change it to be in degree
_targetAngle = Mathf.Atan2(_movementInput.x, _movementInput.y) * Mathf.Rad2Deg + cam.eulerAngles.y;
};
Here is my code but when I use the Arrows to move like the right and up Arrow and I want to release them it detects the smallest difference between the release of the two buttons which is near impossible to avoid (difference between release)
before releasing "_movementInput" contains (0.71, 0.71) but after release it becomes (0, 1) or (1, 0).
I want it to stay (0.71, 0.71) as it was before releasing the two buttons

How to get current frame from Animated Tile/Tilemap.animationFrameRate in Unity

I am using tilemaps and animated tiles from the 2dExtras in unity.
My tiles have 6 frames, at speed=2f, and my tilemap frame rate is 2.
New tiles placed always start on frame 1 and then immediately jump to the current frame of the other tiles already placed, the tilemap is keeping every tile at the same pace, which is working as I want.
However I would like the newly placed tiles to start at the frame the others are currently on,(instead of placing a tile that jumps from frame 1 to frame 4) I would like the new tile to start on frame 4
I've found how to pick the frame I want to start on, however I am having trouble retrieving which frame the animation is currently on, so I was wondering how exactly can I access the current frame of animation of a given tilemap ( Or a given tile, I can create a dummy tile and just read the info out of it, how can I get the current frame of an animated tile? )
The animated tilemaps feature seems to lack the feature to retrieve this information, also when I try tilemap.getsprite it always returns the first frame of the sequence(does not return the sprite currently displayed), and there doesn't seem to be any method to poll info from tilemap.animationFrameRate.
I thought another method would be to set a clock and sync it to the rate of the animation but since I can't get the exact framerate duration the clock eventually goes out of sync.
Any help would be appreciated!
I found a way to solve this question. But it's not 100% insurance.
First of all, I used SuperTile2Unity. That doesn't seem to be the point.
private void LateUpdate()
{
// I use this variable to monitor the run time of the game
this.totalTime += Time.deltaTime;
}
private void func()
{
// ...
TileBase[] currentTiles = tilemap.GetTilesBlock(new BoundsInt(new Vector3Int(0, 0, 0), new Vector3Int(x, y, 1)));
Dictionary<string, Sprite> tempTiles = new Dictionary<string, Sprite>();
//I use SuperTiled2Unity. But it doesn't matter, the point is to find animated tile
foreach (SuperTiled2Unity.SuperTile tile in currentTiles)
{
if (tile == null)
{
continue;
}
if (tile.m_AnimationSprites.Length > 1 && !tempTiles.ContainsKey(tile.name))
{
// find animated tile current frame
// You can easily find that the way SuperTile2Unity is used to process animation is to generate a sprite array based on the time of each frame set by Tiled animation and the value of AnimationFrameRate parameter.
// The length of array is always n times of AnimationFrameRate. You can debug to find this.
tempTiles.Add(tile.name, tile.m_AnimationSprites[GetProbablyFrameIndex(tile.m_AnimationSprites.Length)]);
}
}
//...
}
private int GetProbablyFrameIndex(int totalFrame)
{
//According to the total running time and the total length of tile animation and AnimationFrameRate, the approximate frame index can be deduced.
int overFrameTime = (int)(totalTime * animationFrameRate);
return overFrameTime % totalFrame;
}
I have done some tests. At least in 30 minutes, there will be no deviation in animations, but there may be a critical value. If the critical time is exceeded, there may be errors. It depends on the size of AnimationFrameRate and the accumulation mode of totalTime. After all, we don't know when and how the unity deals with animatedTile.
You could try using implementation presented in [1] which looks as follows:
MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.length * (MyAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1) * MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.frameRate;
[1] https://gamedev.stackexchange.com/questions/165289/how-to-fetch-a-frame-number-from-animation-clip

How to use correctly Scenekit's category mask, collision mask and physicsbody, contactTestBitMask and make proper collisions?

I have been trying to learn scenekit and have finished one book but only collision detection part is not understood, maybe the most important part.
There is category mask, collusion mask and physicsbody?.contactTestBitMask.
I want to create a simple game to get this but I got stuck. I am not sure I get the idea.
In game the game, there is a ball and collects pearls, and stays away from rocks and holes. In this case:
Category masks:
ball = 0b0001
pearls = 0b0010
rocks = 0b0100
holes = 0b1000
physicsBody?.contactTestBitMask:
ball = pearl || rocks // means 0b1110
pearls = 1
rocks = 1
Collusion masks are 1 because they all collide with each other.
I am not sure I get this collision issue. So before I begin to write code, I wanted to be sure. In SCNPhysicsContactDelegate, function below solves how to learn when they contact with each other:
physicsWorld(_ didBegin contact: _) {
var contactNode:SCNNode!
if contact.nodeA.name == "ball" {
contactNode = contact.nodeB
} else {
contactNode = contact.nodeA
}
if contactNode.physicsBody?.categoryBitMask == 0b0010 {
// mean pearls
// raise score etc
}
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000{
if contactNode.name == "Rock" { print("You rocked") }
if contactNode.name == "Hole" { print("You need to climb") }
}
}
I have searched youtube and stack but there is only one explanation.
Stack Link
Youtube videos are not explaining these.
The book examples are copyrighted, so I can't put them on here.
Thank you,
Have a nice day.
You are not using bitwise operators. https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html
change
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000
to
if contactNode.physicsBody?.categoryBitMask == 0b0100 | 0b1000
The single | and & are the correct operators to use for bitwise operations such as these.
There are 3 function to set when you do collision:
mBallBody->setCategoryBitmask(CATEGORY_BALL);
mBallBody->setContactTestBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
mBallBody->setCollisionBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
setCategoryBitmask() --> to set what category this physics belong to!
setContactTestBitmask() --> a mask that defines which categories of bodies cause intersection notifications with this physics body
setCollisionBitmask() --> a mask that defines which categories of phyics bodies can collide with this physics body
so let say ball and wall, ball can collide with wall, so you set contact test bitmask to ball | wall, using | is the bitmask operation.
So you need to set collision category bitmask 1 2 4 8 16 32 and so on.

Unity - Looking through the scope of a gun

Right now I have 2 Cameras: the main camera displays the gun at its normal state and a second camera is attached to the gun (the gun is a child of the main camera) and when toggled it looks through the scope of the gun and increases the field of view.
Heres a visual for a better understanding:
Now if I were to just toggle the second camera on and turn the main camera off, this would work splendid, but it's not very ideal. You should only have 1 camera per scene.
So I want to Lerp the position of the camera to look through the scope and manually decrease the fieldofview. So I have written the following script:
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
CameraTransform.position = Vector3.Lerp (
CameraTransform.position,
CameraTransform.position + ZoomedTransform.position,
5f * Time.deltaTime
);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
The problem with this is that it doesn't work: when I hit the zoom button, the camera speeds through the scene at the speed of light and it's hard to tell exactly what is going on.
Could anyone give me some insight as to what I'm doing wrong? I think it is something to do with the parent-child relationship, but even when I've tried using static values, I cannot seem to replicate the correct solution.
Hierarchy:
(This answer operates under the assumption that ZoomedTransform is a relative transformation, and not the absolute position of the camera as suspected by 31eee384's answer.)
I think there are a couple issues with your code. I'll tackle them individually so they're easier to understand, but they both relate to the following line:
CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);
First, let's look at how you're using Vector3.Lerp(). For the third argument of Vector3.Lerp(), you're supplying 5f * Time.deltaTime. What exactly does this value work out to? Well, the standard framerate is about 60 FPS, so Time.deltaTime = ~1/60. Hence, 5f * Time.deltaTime = 5/60 = ~0.0833.
What is Vector3.Lerp() expecting for the third argument, though? According to the documentation, that third argument should be between 0 and 1, and determines whether the returned Vector3 should be closer to the first or second given Vector3. So yes, 5f * Time.deltaTime falls within this range, but no interpolation will occur - because it will always be around ~0.0833, rather than progressing from 0 to 1 (or 1 to 0). Each frame, you're basically always getting back cameraPos + zoomTransform * 0.0833.
The other notable problem is how you're updating the value of CameraTransform.position every frame, but then using that new (increased) value as an argument for Vector3.Lerp() the next frame. (This is a bit like doing int i = i + 1; in a loop.) This is the reason why your camera is flying across the map so fast. Here is what is happening each frame, using the hypothetical result of your Vector3.Lerp() that I calculated earlier (pseudocode):
// Frame 1
cameraPosFrame_1 = cameraPosFrame_0 + zoomTransform * 0.0833;
// Frame 2
cameraPosFrame_2 = cameraPosFrame_1 + zoomTransform * 0.0833;
// Frame 3
cameraPosFrame_3 = cameraPosFrame_2 + zoomTransform * 0.0833;
// etc...
Every frame, zoomTransform * 0.0833 gets added to the camera's position. Which ends up being a really, really fast, and non-stop increase in value - so your camera flies across the map.
One way to address these problems is to have variables that stores your camera's initial local position, zoom progress, and speed of zoom. This way, we never lose the original position of the camera, and we can both keep track of how far the zoom has progressed and when to stop it.
[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {
private Transform CameraTransform = null;
public Transform ZoomedTransform;
private Vector3 startLocalPos;
private float zoomProgress = 0;
private float zoomLength = 2; // Number of seconds zoom will take
private bool zoomed = false;
void Start () {
CameraTransform = Camera.main.transform;
startLocalPos = CameraTransform.localPosition;
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.LeftShift))
{
zoomProgress += Time.deltaTime;
CameraTransform.localPosition = Vector3.Lerp (startLocalPos, startLocalPos + ZoomedTransform.position, zoomProgress / zoomLength);
CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
}
}
}
Hope this helps! Let me know if you have any questions. This answer does ramble a little, so I hope you don't have any trouble getting the important points from it.
Your lerp target is relative to the camera's current position, so it's constantly moving. This is the target you have:
CameraTransform.position + ZoomedTransform.position
This means that as your camera moves to get closer to this position, the camera's new position causes the destination to change. So your camera keeps moving forever.
Your destination should be ZoomedTransform.position. No addition is necessary because position is in world coordinates. (And when you actually need to convert between spaces, check out TransformPoint and similar methods.)
It has been a while since I have done anything in Unity, but I think it is processing the Lerp function at frame time and not at actual time. You will need to call it in another function that is not being processed at frame time.

How do I show the pointer after touch interaction in Windows 8

When you touch the screen in Windows 8, the mouse pointer is hidden until you move the mouse (or other pointing device). This happens in both desktop and Metro interfaces.
We have a program that allows people to move the mouse pointer using other input methods (such as a joystick), so I need to ensure the mouse pointer is visible.
How can I force the mouse pointer to be displayed?
you can set the flag of touch feedback.
[setting] ->[control panel]->[pen and touch]->[touch feekback] item,there is a checkbox that is [show visual feedback when touch the screen].If you cann't select the checkbox, you can show the mouse pointer without coding anything when touching the screen.
You can inject mouse movement before you start joystick control, using Windows API:
POINT p;
GetCursorPos(&p);
MOUSEINPUT mi;
mi.dx = (LONG) ((p.x * 65535) / screen_width);
mi.dy = (LONG) ((p.y * 65535) / screen_height);
mi.mouseData = 0;
mi.dwFlags = type | MOUSEEVENTF_ABSOLUTE;
mi.time = 0;
mi.dwExtraInfo = NULL;
INPUT input;
input.type = INPUT_MOUSE;
input.mi = mi;
SendInput(1, &input, sizeof(INPUT));