Unity - Change direction along x-axis when GetMouseButtonDown - c#

I'm making a wall-runner type of game, where I need my character to change walls when the mouse button is clicked. I made it work with gravity, but it gave undesired effects. Therefore I'm now working with transform.position, but now the character only moves for a split second (I assume the transform.position only activates while the mouse button is actually clicked).
How do I make it change direction on the mouseclick, instead of it just moving a bit?
Do I need some kind of while loop, or where am I at?
My class:
//Variables used by the Player
public int flyingSpeed;
bool rightWall = true;
bool inAir = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
//Constantly moves the Players position along the Y-axis
if (inAir == false) {
if (Input.GetMouseButtonDown (0) && rightWall == true) {
transform.position += Vector3.left * flyingSpeed * Time.deltaTime;
rightWall = false;
inAir = true;
} else if (Input.GetMouseButtonDown (0) && rightWall == false) {
transform.position += Vector3.right * flyingSpeed * Time.deltaTime;
rightWall = true;
inAir = true;
}
}
}
void OnCollisionEnter2D(Collision2D coll) {
inAir = false;
}

The Input.GetMouseButtonDown method only returns true on the first frame that the button is clicked, so your moving operations are only executed once, which is not enough to switch the walls.
To switch between the walls, you can do one of the following:
Move the player immediately when the mouse is clicked by setting transform.position
Make a function that checks if the player is on the right or left wall (let's call is WallCheck). Then change the rightWall value every time the mouse is clicked.And then add this to your Update method
if (WallCheck() != rightWall)
transform.position += rightWall ? Vector3.left : Vector3.right * flyingSpeed * Time.deltaTime;

Related

Moving Object Not Stopping On Collision Unity

I am developing an infinite tower jumping game using Unity2D, and currently working on a continually moving object which causes the player to die if contact is made. The player can also die if they either fall off of a platform or off-screen. All methods of death rely on a BoxCollider2D I am using as a Killbox (tagged accordingly) - the player sprite has a RigidBody2D and BoxCollider2D attached to it - so there is one attached to the main camera (it moves as the player sprite progresses through the level) and to the top of the moving object.
The current code I have works up to the point where the game over screen appears on player death, but the object continues to move whilst everything else stops.
Here is my code for the moving object:
public class Water : MonoBehaviour {
private Collider2D playerCollider;
public ControllerNew thePlayer;
private float speed = 2f;
public GameManager theGameManager;
//reference scoremanager
private ScoreManager theScoreManager;
bool shouldMove = true;
// Start is called before the first frame update
void Start()
{
theScoreManager = FindObjectOfType<ScoreManager>();
thePlayer = FindObjectOfType<ControllerNew>();
lastPlayerPosition = thePlayer.transform.position;
}
// Update is called once per frame
void Update()
{
if (shouldMove = true)
{
transform.position += Vector3.up * speed * Time.deltaTime;
if (theScoreManager.scoreCount > 100 && shouldMove)
{
transform.position += Vector3.up * (speed+1) * Time.deltaTime;
}
if (theScoreManager.scoreCount > 250 && shouldMove)
{
transform.position += Vector3.up * (speed +2) * Time.deltaTime;
}
if (theScoreManager.scoreCount > 500 && shouldMove)
{
transform.position += Vector3.up * (speed+4) * Time.deltaTime;
}
if (theScoreManager.scoreCount > 1000 && theScoreManager.scoreCount > theScoreManager.hiScoreCount && shouldMove)
{
transform.position += Vector3.up * (speed+5) * Time.deltaTime;
}
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player New")
{
transform.position += Vector3.zero * (speed * 0) * Time.deltaTime;
}
//call object by its tag
if (other.gameObject.tag == "Killbox")
{
shouldMove = false;
if (shouldMove = false){
theGameManager.RestartGame();
transform.position += Vector3.zero * (speed * 0) * Time.deltaTime;
}
}
}
Edit (Issue resolved)
So it turns out that after adding the Water.StopMoving() method into my controller script, the water had not been called as a GameObject in void Start(). Once this was added, the water object stopped on collision.
Just want to say thank you #D.B for your help and bearing with me - apologies if the info I gave wasn't everything you needed to be able to assist me
You made a mistake on the first line of the Update() method :
if (shouldMove = true)
You set the bool to true, not comparing it. Use double = otherwise it will set the bool to true at every frame.
if (shouldMove == true)
By the way you can simplify this part :
//call object by its tag
if (other.gameObject.tag == "Killbox")
{
shouldMove = false;
theGameManager.RestartGame();
transform.position += Vector3.zero * (speed * 0) * Time.deltaTime;
}
(You forgot a = too)
I made a test with this simplified script
void Update()
{
if (shouldMove == true)
{
Debug.Log("move");
transform.position += Vector3.up * speed * Time.deltaTime * GetDifficultyFactor();
}
}
private float GetDifficultyFactor()
{
float factor = 1f;
if(theScoreManager.scoreCount > 100)
{
factor += 1f;
}
if (theScoreManager.scoreCount > 250)
{
factor += 2f;
}
// Add all your speed modification condition here
return factor;
}
void OnTriggerEnter2D(Collider2D other)
{
Debug.Log("trigger");
//call object by its tag
if (other.gameObject.tag == "Killbox")
{
Debug.Log("die");
shouldMove = false;
}
}
And it work fine. Are you sure you have a collider2D set to trigger on your charactert with the tag "Killbox" (with the first letter in uppercase ?). You should have a rigidbody2d on the character too.
Mistake come frome another part of your code or with some trouble with collider2D/tag/RigidBody2D. Without seeing all it's difficult to help you more.
You should try to add some Debug.Log() or use debugeur with breakpoint to be sure code enter into your "die" statement and then not going on the Update if statement. If yes, it's mean you probably set the shouldMove variable in another part of your script.
Answer regarding discussion in comments
I think you want to make this OnTriggerEnter2D logic in both script.
Without seeing all your project I suggest you to make a reference bewteen your character and your water script. Then when the player die the player script will call a method on water to stop it.
public class Player : MonoBehaviour
{
public Water Water;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Killbox")
{
Debug.Log("die");
Water.StopMoving();
}
}
}
public class Water : MonoBehaviour
{
private bool shouldMove;
public void Update()
{
...
}
public void StopMoving()
{
shouldMove = false;
}
// No Trigger logic here
}

How to make a wallrun in unity

I`m trying to make a wallrun in unity like the wallrun of the Prince of Persia.
The idea is that when you are touching the wall and you press "left shift" the player would run some seconds without falling.
I would like to add that if you press "enter" the character would jump to the side using the Wall as support for impulse.
You can watch this in the first minutes of the video:
https://www.youtube.com/watch?v=zsnB7HEiLr0
I have made this script for the character controller:
public Transform playerPosition;
//controls the x movement. (right/left)
public float horizontalmove;
//controls the y movement. (forward/back)
public float verticalmove;
//controls the movement direction.
private Vector3 playerInput;
//Here I store my character controller.
public CharacterController player;
//controls the player speed.
public float playerSpeed;
//controls de movement direction according to camera
public Vector3 movePlayer;
//controls the last movement
public Vector3 lastMovePlayer;
public float gravity = 9.8f;
public float fallVelocity;
public float jumpForce = 5.0f;
public float verticalSpeed;
private RaycastHit HitR;
private RaycastHit HitL;
//Here I store the main camera
public Camera mainCamera;
//It stores the camera direction when the player is looking forward.
private Vector3 camForward;
//It stores the camera direction when the player is looking right.
private Vector3 camRight;
//Checks
//The meaning of Caida is fall.
public bool Caida;
//The meaning of salto is jump.
public bool Salto;
public bool Wallrun;
public bool WallrunCount;
// Start is called befoe the first frame update
void Start()
{
//i store the character controler.
player = GetComponent<CharacterController>();
Caida = true;
}
// Update is called once per frame
void Update()
{
if (Wallrun == true)
{
Caida = false;
}
if (Salto == true)
{
fallVelocity -= gravity * Time.deltaTime;
movePlayer = lastMovePlayer;
}
if (Caida == true)
{
fallVelocity -= gravity * Time.deltaTime;
}
if (player.isGrounded && Wallrun == false)
{
Caida = false;
Salto = false;
WallrunCount = false;
//I assign the horizontal move to the w and s keys.
horizontalmove = Input.GetAxis("Horizontal");
//I assign the vertical move to the a and d keys.)
verticalmove = Input.GetAxis("Vertical");
//controls the movement direction
playerInput = new Vector3(horizontalmove, 0, verticalmove);
//limits the player speed. With this method if teh player waalks in diagonal doesn´t
//exceed the speed limit.
playerInput = Vector3.ClampMagnitude(playerInput, 1);
// It calls the function that give the camera look direction.
camDirection();
//Here, It`s calculates the player movement considering the camera point and the movement
//we have assing to teh player earlier
//With this method the player always moves looking to the camera
movePlayer = playerInput.x * camRight + playerInput.z * camForward;
//The movement * the speed we want.
movePlayer = movePlayer * playerSpeed;
//we are going to say to the player where is looking at.
player.transform.LookAt(player.transform.position + movePlayer);
//It gives the gravity to the player.
fallVelocity = -gravity * Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Space))
{
Salto = true;
fallVelocity = jumpForce;
}
}
else if (!player.isGrounded && Salto == false && Wallrun == false)
{
Caida = true;
}
movePlayer.y = fallVelocity;
//we give the movement to th eplayer.
player.Move(movePlayer * Time.deltaTime);
lastMovePlayer = movePlayer;
}
private void OnTriggerStay(Collider other)
{
if (Input.GetKeyDown(KeyCode.LeftShift) && Wallrun == false && WallrunCount == false)
{
if (Input.GetKey("w"))
{
Wallrun = true;
WallrunCount = true;
fallVelocity = 5f;
movePlayer.y = fallVelocity;
movePlayer.z = movePlayer.z * 1.6f;
if (Physics.Raycast(transform.position, transform.right, out HitR, 1))
{
movePlayer.x = 1.6f;
}
else if (Physics.Raycast(transform.position, -transform.right, out HitL, 1))
{
movePlayer.x = -1.6f;
}
StartCoroutine(wallrunTime());
}
}
}
void camDirection()
{
//we store the forward and right directions here.
camForward = mainCamera.transform.forward;
camRight = mainCamera.transform.right;
//we block the direction and the camera direction because we are not going to use it.
camForward.y = 0;
camRight.y = 0;
//It gives as the normalized vectors.
camForward = camForward.normalized;
camRight = camRight.normalized;
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (!player.isGrounded && hit.normal.y < 0.1f)
{
if (Input.GetKeyDown(KeyCode.Space))
{
fallVelocity = jumpForce;
movePlayer = hit.normal * 7;
player.transform.LookAt(player.transform.position + movePlayer);
}
}
}
IEnumerator wallrunTime()
{
yield return new WaitForSeconds(1);
Wallrun = false;
}
As you can see, when the player enters the leftshift it checks to what direction is moving the character, if this is front (w) the script make the z movement * 1.6 (to make the character run a bit in the wall) and the character go a bit up bit the y axis.Then, the script checks if the Wall it i son the right or on the left and, depending on where the wall is, sticks the character to that wall.
private void OnTriggerStay(Collider other)
{
if (Input.GetKeyDown(KeyCode.LeftShift) && Wallrun == false && WallrunCount == false)
{
if (Input.GetKey("w"))
{
Wallrun = true;
WallrunCount = true;
fallVelocity = 5f;
movePlayer.y = fallVelocity;
movePlayer.z = movePlayer.z * 1.6f;
if (Physics.Raycast(transform.position, transform.right, out HitR, 1))
{
movePlayer.x = 1.6f;
}
else if (Physics.Raycast(transform.position, -transform.right, out HitL, 1))
{
movePlayer.x = -1.6f;
}
StartCoroutine(wallrunTime());
}
}
}
With this method, I can make the character jump when the player enters space because the character is hitting the wall (a condition required to bounce in the wall).
And after some seconds, the character falls simulating a wallrun.
IEnumerator wallrunTime()
{
yield return new WaitForSeconds(1);
Wallrun = false;
}
The problem is that this works perfectly if the character makes the wallrun when is looking in the same direction as the axes of the environment.
When the character z axis is looking at the same direction of the environment z axes it works perfectly. But when the axes are not looking at the same direction is a disaster. I show you a video I have recorded.
https://youtu.be/KH7rE9kh5d0
The problem, I suppose, is that with the code I have written, I'm moving the character according to the axes of the environment, and not its axes, so I have to tell him to move according to his own.
My teacher says I might have to change the way I make the character move. I would like to know if you can tell me a way to fix this without changing the movement method or if it is not possible, how can I change that movement.
Thank you in advance.

Why my enemy not moving towards target

Here is a Video also - http://tinypic.com/r/mmagki/9
Here is my start () function
void Start()
{
target = GameObject.FindGameObjectWithTag("Player").transform;
}
and update() function
void Update()
{
transform.LookAt(target);
float step = speed * Time.deltaTime;
distance = (transform.position - target.position).magnitude;
//Debug.Log("Now distance -" + distance);
if (distance < 20)
{
// print("In Range");
transform.GetComponent<Animation>().Play("attack", PlayMode.StopAll);
if (isAttacking == false)
{
isAttacking = true;
Hit.playerHealth -= Random.Range(20f, 25f) * Time.deltaTime;
Hit.playerHealth -= Random.Range(20f, 25f);
// StartCoroutine(MyCoroutine(4));
// print("Player Health Status = " + Hit.playerHealth);
if (Hit.playerHealth <= 0)
{
// print("Player dead");
}
}
else
{
}
}
else
{
// print("Out of Range");
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
transform.GetComponent<Animation>().Play("walk", PlayMode.StopAll);
}
}
My Zombie (Enemy) is approaching Player, when Zombie gets hit with the wall, he should go to gate.
What i did, as soon as Zombie (Set Trigger = checked) hit with the wall, i have changed the reference of 'target' to Object with tag 'gate'.
Now the Zombie is not moving towards gate object (i have set tag 'gate' also). he is still moving towards player only. Not able to change the reference of target.
void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "wall")
{
target = GameObject.FindGameObjectWithTag("gate").transform;
Debug.Log("Yes its a onTrigger Enter function , hitting with wall");
}
}
It looks like you don't have a collider on your gate. Adding a collider to the gate should do the trick.
first verify the tags of all are seted correctly, then you need to add a rigidbody component at least in one of the objects as Unity Docs say: OnTriggerEnter . I recomend to add it to the zombie and set UseGravity to false. and with this it should work... sorry for bad english.

Trouble getting full Jump animation to play while airborne

I'm trying to control a set of character animations but I'm having trouble with the jump animations. My horizontal walk animations are working fine but when I hit the jump button I want one of two jump animations to play depending on whether the player was in an idle or walking state.
I've set up a bool that updates based on whether my character collider is contacting the ground and I check against to determine when to play the jump animation. However when I press the jump button currently it only plays the first frame of the respective jump animation and then goes right back to either idle or walk. How do I get the entire jump animation to play when the jump button is pressed and the character is airborne?
using UnityEngine;
using System.Collections;
public class AsherBlackMover : MonoBehaviour {
public float moveSpeed = 3;
public float rotateSpeed = 10;
public Transform graphics;
public SkeletonAnimation skeletonAnimation;
public Vector2 jumpVector;
public bool isGrounded;
// isGrounded Variables
public Transform grounderPosition;
public float grounderRadius;
public LayerMask grounderLayerMask;
private Rigidbody2D asherBlackRB;
private Animator asherBlackAnim;
Quaternion goalRotation = Quaternion.identity;
float xDir;
string currentAnimation = "";
void Start ()
{
// Create a reference to Asher Black Rigidbody2D
asherBlackRB = GetComponent<Rigidbody2D>();
}
void Update ()
{
xDir = Input.GetAxis ("Horizontal") * moveSpeed;
if (xDir > 0 && isGrounded == true) // ------ Walk Right
{
goalRotation = Quaternion.Euler (0, 0, 0);
SetAnimation ("Walk", true);
}
else if (xDir < 0 && isGrounded == true) // ------ Walk Left
{
goalRotation = Quaternion.Euler (0, 180, 0);
SetAnimation ("Walk", true);
}
else if (isGrounded == true) // ------ Idle
{
SetAnimation ("Idle", true);
}
// Jump Button
if (Input.GetKeyDown("space") && isGrounded == true)
{
isGrounded = false;
skeletonAnimation.state.SetAnimation (0, "Idle-Jump", true);
if (xDir > 0 && isGrounded == false)
{
Jump ("Walk-Jump");
}
else if (xDir < 0 && isGrounded == false)
{
Jump ("Walk-Jump");
}
else if (isGrounded == false)
{
Jump ("Idle-Jump");
}
}
// Circle on character that determines when grounded or not
isGrounded = Physics2D.OverlapCircle (grounderPosition.transform.position, grounderRadius, grounderLayerMask);
// Flip character smothly to emulate paper mario effect
graphics.localRotation = Quaternion.Lerp (graphics.localRotation, goalRotation, rotateSpeed * Time.deltaTime);
}
void OnDrawGizmos ()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere (grounderPosition.transform.position, grounderRadius);
}
void SetAnimation (string name, bool loop)
{
if (name == currentAnimation)
{
return;
}
skeletonAnimation.state.SetAnimation (0, name, loop);
currentAnimation = name;
}
void Jump (string animName)
{
asherBlackRB.AddForce (jumpVector, ForceMode2D.Force);
SetAnimation (animName, true);
print ("Doing a jump using the " + animName + " animation");
}
// Physics Updates
void FixedUpdate ()
{
asherBlackRB.velocity = new Vector2 (xDir, asherBlackRB.velocity.y);
}
}
From what I can tell, in the // Jump Button if block you set isGrounded to false, but right after that you set isGrounded based on collision detection:
isGrounded = Physics2D.OverlapCircle (grounderPosition.transform.position, grounderRadius, grounderLayerMask);
When you get to this statement you are still processing the same frame and the character still didn't have time to move isGrounded will be true by the time you get to the end of the update function. On the next update the animation will be reverted to the walk/idle one by the first set of if/else blocks. You might want to put the collision detection statement in an else block for the jump if. This might not be enough though as the time between frames might not be long enough for the character to move far enough away from the ground. In that case you might hold a counter and count, say, 5 frames until you go back to check the collision with the ground.

Smoothly lerp a rigidbody

I'm wanting my player to move to the left when the player hits the left of the screen. Right now, my player only moves as and when I hold down my mouse button. What I'm really wanting is for him to continually move to the left until he hits the target position when I press the screen once.
Can someone please tell me what I'm missing from my code to allow this to happen?
void FixedUpdate()
{
if(Input.GetMouseButtonDown(0))
{
if(Input.mousePosition.x < (Screen.width*2)/3 && Input.mousePosition.y > Screen.height/3)
{
if(position == middle)
{
MoveLeft();
}
}
}
}
void MoveLeft()
{
var pos = rigidbody.position;
float xPosition = left.transform.position.x;
pos.x = Mathf.Lerp(pos.x, xPosition, speed * Time.deltaTime);
rigidbody.position = pos;
}
My method is in the FixedUpdate because I'm moving the players rigidbody as oppose to translating the actual player.
Right now the player only moves when you hit mouse button because that's how your code is written: you check if the mouse is pressed every frame, and only if it is move the rigidbody.
If you want the player to move regardless of whether the mouse is still pressed or not, you should create some kind of variable to save the state of the player, and set to move left when the mouse button is pressed and set it to stop when the player reaches his target.
If I correctly understood your goal, it would look something like this:
bool moveLeft = false;
void FixedUpdate()
{
if(Input.GetMouseButtonDown(0)
&& (Input.mousePosition.x < (Screen.width*2)/3 && Input.mousePosition.y > Screen.height/3))
{
moveLeft = true;
}
if (moveLeft
&& (position == middle))
{
MoveLeft();
}
else
{
moveLeft = false;
}
}
void MoveLeft()
{
var pos = rigidbody.position;
float xPosition = left.transform.position.x;
pos.x = Mathf.Lerp(pos.x, xPosition, speed * Time.deltaTime);
rigidbody.position = pos;
}

Categories

Resources