I'm working on a snake game and I'm running into a problem where if I hit my inputs (up, down, left, right) too fast, my snake runs into itself. I've tried to create a timer under Update() to regulate my movements but I find my snake to still be running into itself from time to time.
I use InvokeRepeating() to move my snake every 0.05 seconds and I have 2 methods of inputs: 1) Update to listen for keyboard inputs (up, down, left, right arrows) and 2) a Gamepad UI (up, down, left, right buttons). Any suggestions to how I could regulate my directional changes?
void Start () {
Screen.orientation = ScreenOrientation.Portrait;
isPaused = false;
InvokeRepeating("TimerInvoke", 0, deltaTimer);
}
void TimerInvoke() {
Movement();
StartCoroutine(checkVisible());
if (currentSize == maxSize)
TailFunction();
else
currentSize++;
}
void Update () {
CompDirectionControl();
PauseCMD();
}
void CompDirectionControl() {
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
direction = Direction.North;
if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
direction = Direction.East;
if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
direction = Direction.South;
if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
direction = Direction.West;
}
public void MPadDirectionControl(int numDirection)
{
if (direction != Direction.South && numDirection == 0)
direction = Direction.North;
if (direction != Direction.West && numDirection == 1)
direction = Direction.East;
if (direction != Direction.North && numDirection == 2)
direction = Direction.South;
if (direction != Direction.East && numDirection == 3)
direction = Direction.West;
}
I tried creating something like this but the delay is very inconsistent
if (Time.time >= timeStamp)
{
CompDirectionControl();
timeStamp = Time.time + moveInterval;
}
UPDATE
I was able to add a delay to my keyboard inputs by using a coroutine. However, I have not figured out how to do the same with my button UI inputs. If I create a public IEnumerator GamePadDirControl(int direction), how do I pass my button's parameter to my IEnumator? Even though I set my GamePadDirControl to public, I don't see it in my Inspector so I can't link my button to it
IEnumerator DirControl() {
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.North;
}
else if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.East;
}
else if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.South;
}
else if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.West;
}
}
UPDATE 2
For my buttons UI - Since it isn't possible for my button to link up with IEnumator functions, I decided to create individual coroutines for each direction and call on them from my public void MPadDirectionControl(int numDirection) function.
updated code on button ui input
public void MPadDirectionControl(int numDirection)
{
if (direction != Direction.South && numDirection == 0)
{
StartCoroutine(IntervalN());
//direction = Direction.North;
}
else if (direction != Direction.West && numDirection == 1)
{
StartCoroutine(IntervalE());
//direction = Direction.East;
}
else if (direction != Direction.North && numDirection == 2)
{
StartCoroutine(IntervalS());
//direction = Direction.South;
}
else if (direction != Direction.East && numDirection == 3)
{
StartCoroutine(IntervalW());
//direction = Direction.West;
}
}
IEnumerator IntervalN() {
yield return new WaitForSeconds(deltaTimer);
direction = Direction.North;
}
IEnumerator IntervalE()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.East;
}
IEnumerator IntervalS()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.South;
}
IEnumerator IntervalW()
{
yield return new WaitForSeconds(deltaTimer);
direction = Direction.West;
}
Not sure i fully understand your question ...
Your trying to change the direction before a delay ?
If so, I will try:
StartCoroutine(ChangeDirectionTo(Direction.North, 0.05f));
IEnumarator ChangeDirectionTo(Direction direction, float delay) {
yield return new WaitForSeconds(delay);
this.direction = direction;
}
I think I see the problem. You need to distinguish between the current direction of the snake, and the user's commanded direction. Try something like this:
if (direction != Direction.South && Input.GetKeyDown(KeyCode.UpArrow))
nextDirection = Direction.North;
if (direction != Direction.West && Input.GetKeyDown(KeyCode.RightArrow))
nextDirection = Direction.East;
if (direction != Direction.North && Input.GetKeyDown(KeyCode.DownArrow))
nextDirection = Direction.South;
if (direction != Direction.East && Input.GetKeyDown(KeyCode.LeftArrow))
nextDirection = Direction.West;
}
Then in the code where you actually create the cube, just before spawning it, use nextDirection to tell you where the player wants to go:
direction = nextDirection;
Thus the player simply doesn't have the chance to change the direction before the cube is spawned.
This should work, but it has the disadvantage that you won't get the nice "input buffering" effect that some snake games have, where you can tap Right, Down while the snake is going north to do a U-turn. If you want to do that, you'll have to implement some kind of smart buffering system that detects if the player puts in a very specific kind of "invalid" input (north/south when direction is north and nextDirection is east) and sets the snake up for a future direction change. Kind of like buffering "special move" inputs for a fighting game.
Related
I'm making a simple text based game where the user is put in a cave and based on what room he enters and what direction he's facing he would get a description of what he senses. I have gotten it to work with his starting position and moving him forward but for some reason he won't turn.
Point pos = new Point(0, 0);
Point dirE = new Point(1, 0); //EAST
Point dirS = new Point(0, 1); //South
Point dirN = new Point(-1, 0); //North
Point dirW = new Point(0, -1); //WEST
Point playerDir = dirE;
Boolean alive = true;
// Point playerDir = dirE;
// Boolean alive = true;
Point nextRoom = pos.add(playerDir);
while (alive == true)
{
String cmd = Console.ReadLine().ToUpper();
//Directions
if (playerDir == dirE && cmd == "R")
{
playerDir = dirS;
}
if (cmd == "F")
{
if (playerDir == dirW && pos.x > 0)
{
pos = nextRoom;
}
if (playerDir == dirE && pos.x < 3)
{
pos = nextRoom;
}
if (playerDir == dirS && pos.y < 3)
{
pos = nextRoom;
}
if (playerDir == dirN && pos.y > 0)
{
pos = nextRoom;
}
try
{
nextRoom = pos.add(playerDir);
if (board[nextRoom.x, nextRoom.y] == "L")
{
Console.WriteLine("You Feel Heat");
}
else if (board[nextRoom.x, nextRoom.y] == "N")
{
Console.WriteLine("You feel nothing");
}
else if (board[nextRoom.x, nextRoom.y] == "G")
{
Console.WriteLine("You see a small glow");
}
else if (board[nextRoom.x, nextRoom.y] == "S")
{
Console.WriteLine("You smell rust");
}
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("You can not move forward there a wall in front of you");
}
}
I have tried to check South and thought I possibley wrote the cords wrong and swapped it to 0,-1 which did nothing and I also attempted to change int the if statement from
playerDir = dirS; to playerDir = pos.add(dirS) and still nothing
I suspect that my logic is wrong despite being told that this should work by someone more knowledgeable.
Edit~
It should be noted that I am currently testing moving east and then turning to move south.
My point class if that helps understand my current code
class Point
{
public int x;
public int y;
public Point(int tx, int ty)
{
x = tx;
y = ty;
}
public Point add(Point other)
{
int newX = this.x + other.x;
int newY = this.y + other.y;
return new Point(newX, newY);
}
}
I think I see the problem here...
// This line sets the next room that the player will move to
Point nextRoom = pos.add(playerDir);
while (alive == true)
{
String cmd = Console.ReadLine().ToUpper();
// This sets the direction that the player will move in
if (playerDir == dirE && cmd == "R")
{
playerDir = dirS;
}
// This supposedly sets the player's next room, but that was set before we decided what direction he was facing
if (cmd == "F")
{
if (playerDir == dirW && pos.x > 0)
{
pos = nextRoom;
}
// remaining code omitted for brevity
Try changing the order of execution, so that the player turns before working out which room to move to, e.g.
while (alive == true)
{
String cmd = Console.ReadLine().ToUpper();
// This sets the direction that the player will move in
if (playerDir == dirE && cmd == "R")
{
playerDir = dirS;
}
// This line sets the next room that the player will move to
Point nextRoom = pos.add(playerDir);
// This should move the player to the correct room
if (cmd == "F")
{
if (playerDir == dirW && pos.x > 0)
{
pos = nextRoom;
}
// remaining code omitted for brevity
It started happening after I wrote the script for dashing. Now, whenever I dash into walls, my character gets stuck to that wall. If I dash towards the roof, then I can walk on the roof for example. Also, when I dash next to a wall / on the floor, it never stops dashing after a single press. Any help will be appreciated, thanks.
The code below is the dashing script, if needed I can post the variables set and my character controller script too.
void Start()
{
_rigidbody = GetComponent<Rigidbody2D>();
dashTime = baseDashTime;
}
void Update()
{
isGrounded = Physics2D.OverlapCircle(feet.position, checkRadius, checkGround);
if(isGrounded == true)
{
dashTime = baseDashTime;
}
if (direction == 0)
{
if (Input.GetKey(KeyCode.W) && (Input.GetKeyDown(KeyCode.LeftShift)))
{
direction = 1;
}
else if (Input.GetKey(KeyCode.A) && (Input.GetKeyDown(KeyCode.LeftShift)))
{
direction = 2;
}
else if (Input.GetKey(KeyCode.S) && (Input.GetKeyDown(KeyCode.LeftShift)))
{
direction = 3;
}
else if (Input.GetKey(KeyCode.D) && (Input.GetKeyDown(KeyCode.LeftShift)))
{
direction = 4;
}
}
else
{
if (dashTime <= 0)
{
direction = 0;
_rigidbody.velocity = Vector2.zero;
}
else
{
dashTime -= Time.deltaTime;
if (direction == 1)
{
_rigidbody.velocity = Vector2.up * dashSpeed;
}
else if (direction == 2)
{
_rigidbody.velocity = Vector2.left * dashSpeed;
}
else if (direction == 3)
{
_rigidbody.velocity = Vector2.down * dashSpeed;
}
else if (direction == 4)
{
_rigidbody.velocity = Vector2.right * dashSpeed;
}
}
}
}
In your code you have this statement in the update function. When you dash on the ground, im guessing IsGrounded will stay true, so the player will keep dashing forever. This is probably what causes the player to be stuck.
if(isGrounded == true)
{
dashTime = baseDashTime;
}
My problem is for a wall jump.
I basically want my wall jump to have a bit of force away from to wall to make it look and feel better than just jumping up straight.
The problem is, when I add a force on the x axis, it does not register when I am also holding the arrow towards the wall. If I am not holding down the walking button, then press jump, then the x force works.
I have a feeling that the horizontal axis force is competing with the opposite force that I want to add to the player when he hits the jump button.
Walking code:
private void ApplyMovement()
{
if (isGrounded)
{
myRigidbody.velocity = new Vector2(movementSpeed * movementInputDirection, myRigidbody.velocity.y);
}
else if (!isGrounded && !isWallSliding && movementInputDirection != 0)
{
Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);
myRigidbody.AddForce(forceToAdd);
if (Mathf.Abs(myRigidbody.velocity.x) > movementSpeed)
{
myRigidbody.velocity = new Vector2(movementSpeed * movementInputDirection, myRigidbody.velocity.y);
}
}
else if (!isGrounded && !isWallSliding && movementInputDirection == 0)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x * airDragMultiplier, myRigidbody.velocity.y);
}
if (isWallSliding)
{
if (myRigidbody.velocity.y < -wallSlideSpeed)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, -wallSlideSpeed);
}
}
}
Jump code:
private void Jump()
{
if (canJump && !isWallSliding)
{
myRigidbody.velocity = new Vector2(myRigidbody.velocity.x, jumpForce);
numberOfJumpsLeft--;
}
else if ((isWallSliding || isTouchingWall) && !isGrounded && canJump)
{
isWallSliding = false;
numberOfJumpsLeft--;
Vector2 forceToAdd = new Vector2(wallPushForce * -facingDirection, wallJumpForce *
wallJumpDirection.y);
myRigidbody.AddForce(forceToAdd, ForceMode2D.Impulse);
}
}
How do you add multi-tap (not multi-touch!) input to a Unity3D game? I have a hard time finding any useful information about this.
What I got so far is multi-touch input support. this is what I'm using for that:
private Vector2 _touchOrigin = -Vector2.one;
public bool TouchEnded(int touchCount = 1)
{
if (Input.touchCount != touchCount) return false;
Touch lastTouch = Input.GetTouch(touchCount - 1);
if (lastTouch.phase == TouchPhase.Began)
{
_touchOrigin = lastTouch.position;
}
else if (lastTouch.phase == TouchPhase.Ended && _touchOrigin.x >= 0)
{
_touchOrigin.x = -1;
return true;
}
return false;
}
What I would like to do is write a similar method but for multi-tap. I.e. the user has to tap several fingers (touchCount) simultaneously a number of times (tapCount). This would be the method signature:
public bool TapEnded(int touchCount = 1, int tapCount = 2)
{
}
Can somebody give me some help how to get this requirement working?
You can use Input.Touches Returns list of objects representing status of all touches during last frame. reference to link http://docs.unity3d.com/ScriptReference/Input-touches.html
void Update() {
int fingerCount = 0;
foreach (Touch touch in Input.touches) {
if (touch.phase != TouchPhase.Ended && touch.phase != TouchPhase.Canceled)
fingerCount++;
}
if (fingerCount > 0)
print("User has " + fingerCount + " finger(s) touching the screen");
}
I have a camera in my scene I am controlling with a third party device. If I spin the device, pitch it forward etc the same action is replicated with my camera. However, I'm stuck working out how to always make sure my camera is facing forward and moving forward in spite of its Y rotation.
For example, my camera starts at point 0,0,0 with the same rotation value. If I rotate my camera 90 degrees (either side) and move my camera forward, my view now moves side ways. How can I get it so that where ever my camera is facing, that is my forward direction?
Here is my code so far:
private void MoveForward()
{
Debug.Log("yaw: " + yaw + " pitch: " + pitch + " roll: " + roll);
if( pitch > maxPitch.x && pitch < maxPitch.y && reversePitch == false)
{
camera.rigidbody.AddForce(0, 0,-pitch);
// camera.transform.position += new Vector3(0,0, -pitch);
}
else if ( pitch > maxPitch.x && pitch < maxPitch.y && reversePitch == true)
{
camera.rigidbody.AddForce(0, 0, pitch);
// camera.transform.position += new Vector3(0,0, pitch);
}
else
{
camera.rigidbody.velocity = Vector3.zero;
// camera.transform.position = new Vector3(0,0,0);
}
}
private void MoveSideways()
{
if( roll > maxRoll.x && roll < maxRoll.y && reverseRoll == false)
{
camera.rigidbody.AddForce( roll, 0, 0);
}
else if ( roll < maxRoll.x && roll > maxRollNegative && reverseRoll == false)
{
camera.rigidbody.AddForce( roll, 0 ,0);
}
else if( roll > maxRoll.x && roll < maxRoll.y && reverseRoll == true)
{
camera.rigidbody.AddForce( -roll, 0, 0);
}
else if ( roll < maxRoll.x && roll > maxRollNegative && reverseRoll == true)
{
camera.rigidbody.AddForce( -roll, 0 ,0);
}
else
{
camera.rigidbody.velocity = Vector3.zero;
}
}
private void RotateAround()
{
if(reverseYaw == true)
{
target = Quaternion.Euler(0, yaw,0);
}
else
{
target = Quaternion.Euler(0, -yaw,0);
}
transform.rotation = Quaternion.Slerp(camera.transform.rotation, target, Time.time *rotationSpeed);
}
This occurs because Rigidbody.AddForce takes a world space vector, but you are giving it a local space vector. Use Transform.TransformDirection to convert your vector from local space to world space.
Vector3 force = transform.TransformDirection(0, 0, pitch);
camera.rigidbody.AddForce(force);