My script is about when my ball hit a "Trap Object", it'll be moved to start position and STOP right there. How to do that?
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag ( "Trap" ))
{
//move object to start position
transform.position = startposition.transform.position;
// I want to stop the object here, after it was moved to start position. Because my ball was moving when it hit Trap object, so when it was moved to start position, it keeps rolling.
}
}
As I mentioned in my comment, you need to reset the force of the rigidbody to make sure that your ball is stopped completely. The following code could fix your issue.
// LateUpdate is triggered after every other update is done, so this is
// perfect place to add update logic that needs to "override" anything
void LateUpdate() {
if(hasStopped) {
hasStopped=false;
var rigidbody = this.GetComponent<Rigidbody>();
if(rigidbody) {
rigidbody.isKinematic = true;
}
}
}
bool hasStopped;
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag ( "Trap" ))
{
var rigidbody = this.GetComponent<Rigidbody>();
if(rigidbody) {
// Setting isKinematic to False will ensure that this object
// will not be affected by any force from the Update() function
// In case the update function runs after this one xD
rigidbody.isKinematic = false;
// Reset the velocity
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
hasStopped = true;
}
//move object to start position
transform.position = startposition.transform.position;
// I want to stop the object here, after it was moved to start position. Because my ball was moving when it hit Trap object, so when it was moved to start position, it keeps rolling.
}
}
The code is untested, so I wouldnt be suprised if it didnt compile on first try, I could have misspelled Rigidbody or something.
(I don't have Unity at work either so hard to test ;-))
Hope it helps!
Do you add some form of speed or velocity to your ball? If you do, you need to reset this to zero to stop your ball from rolling.
Related
In my game I've a case that when the player is touching the collider of a specific GameObject and the player press the X key I want to change his position to the position of another GameObject.
To do that I use the following code
void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.X))
{
// Check Colliders collition
// End of Check Colliders collition
var playerRigidBody = GetComponent<Rigidbody2D>();
playerRigidBody.position = AnotherObject.transform.position;
}
}
I've realized that very often when I press the X key the player don't change his position to the position of the other GameObject. However if I move the code to the Update() method it works as expected (with no delay).
But I've read in the official documentation and in a lot of blogs that the code related to physics and Rigidbodies must be put in the FixedUpdate instead of Update
So the question is the following:
For this case putting the code in the Update() method is not recommended? (Note that I'm not using forces nor torques)
If it's not recommended then how can I assure that every time the player press the X key he will change his position to the other GameObject position in FixedUpdate()?
In general if you want a smooth movement you should rather use MovePosition in FixedUpdate otherwise everything regarding Physics (so also RigidBody) you should always do in FixedUpdate (as explained in the API there as well).
You should however catch the GetKeyDown in Update and split those two things:
bool wasClicked;
RigidBody playerRigidBody;
void Start()
{
// do this only once!
playerRigidBody = GetComponent<Rigidbody2D>();
}
void Update()
{
// Catch user input here
if (Input.GetKeyDown(KeyCode.X))
{
wasClicked = true;
}
}
void FixedUpdate()
{
// Handle physics here
if(!wasClicked) return;
playerRigidBody.position = AnotherObject.transform.position;
wasClicked = false;
}
So, I'm still not the best at this but I'm trying to use this script for 2D movement but the jumping isn't working for some reason. It keeps saying that the OnCollisionEnter function "is declared but never used". Can someone tell me what im doing wrong? Thanks
If I remove the (Collision col) part it says that "void cannot be used in this context".
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RonyWalking : MonoBehaviour
{
Rigidbody2D rigid;
SpriteRenderer sprite;
public bool isJumping;
public float spd = 2.0f;
// Start is called before the first frame update
void Start()
{
rigid = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(Input.GetKey("d")){rigid.velocity = new Vector2(spd, 0);}
else if(Input.GetKey("a")){rigid.velocity = new Vector2(-spd, 0);}
if(Input.GetKeyDown("w") && isJumping == false)
{
rigid.velocity = new Vector2(0, 5);
isJumping = true;
}
void OnCollisionStay(Collision col)
{
isJumping = false;
}
}
}
When using 2D physics, you need to use the 2D lifecycle methods;
void OnCollisionStay2D(Collision2D col)
{
isJumping = false;
}
And you shouldn't put this method inside your Update method... It should be on class level:
public class RonyWalking
{
void Update()
{
// ...
}
void OnCollisionStay2D(Collision2D col)
{
// ...
}
}
Don't worry about "Is declared but never used", this may be because you don't have specific code referencing the method, but Unity will raise events that calls it, "automagically"
Another thing that I can see while reading your code, that may be unintentional behaviour for you, is that when clicking left/right, you set velocity UP to 0, and when clicking up you set LEFT/RIGHT velocity to 0; this will result in freezing the movement mid-air if you jump, then move while in air:
Click D; velocity = 2, 0
Click W; velocity = 0, 5
Character will now move upwards until another input is given
Click D; velocity = 2, 0 and the character will continue moving while in air because when moving sideways the up/down velocity is set to 0
To solve this, either set the other to existing velocity or make the inputs manipulate a Vector that you then apply at the end of the movement code:
Vector2 existingMovement = rigid.velocity;
if (Input.GetKey(KeyCode.D))
existningMovement.x = spd;
else if (Input.GetKey(KeyCode.A))
existningMovement.x = -spd;
if (Input.GeyKeyDown(KeyCode.W) && !isJumping)
{
existningMovement.y = 5f;
isJumping = true;
}
Furthermore, I think you may have some unexpected behaviour with OnCollisionStay; it will fire every frame that you're colliding with the ground, I assume. But I think it may also fire a frame or two AFTER you've jumped since the physics of your character will not INSTANTLY leave the collision, so isJumping will be set to false even after your jump, letting you jump while in the air one more time.
I would recommend that you use OnCollisionExit2D(Collision2D col) to set isJumping = true instead, or OnCollisionEnter2D(Collision2D col) and set it to isJumping = false, depending on the functionality you desire (if you want the ability to jump after walking out of a cliff)
So basically, after hours of torment trying to create basic movement script for simple platformer game I succeeded, but not quite. Square character is able to move around and jump just ok, but sometimes it won't jump, usually while moving on short distances or, rarely, standing in place and trying to jump. I can't figure out how to fix that. Here is entire script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
private Rigidbody2D rgdb2;
public float movementSpeed;
public float jumpHeight;
private bool isJumping = false;
// Use this for initialization
void Start ()
{
rgdb2 = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate ()
{
float moveHorizontal = Input.GetAxis("Horizontal");
HandleMovement(moveHorizontal);
if (Input.GetKeyDown(KeyCode.Space) && isJumping == false)//by typing Space player jumps, cant double-jump
{
rgdb2.AddForce(new Vector2(rgdb2.velocity.x, 1 * jumpHeight), ForceMode2D.Impulse);
isJumping = true;
Debug.Log("jumped");
}
}
private void HandleMovement(float moveHorizontal)//applying player horizontal controls and customing player's speed by movementSpeed variable
{
rgdb2.velocity = new Vector2(moveHorizontal * movementSpeed, rgdb2.velocity.y);
}
private void OnCollisionEnter2D(Collision2D coll)
{
if (coll.transform.tag == "Platform") //if player is touching object with Platform tag, he can jump
{
Debug.Log("on ground bitch");
isJumping = false;
}
}
}
It may not be that important, but I want to polish this game as much as possilble, even if I don't need to, since it's basically my first game made in Unity3d with C#.
An important thing to keep in mind: Unity3D Engine's inputs are only updated during the time the engine calls Update() methods for your GameObjects.
What this means is that you should not read any type of input in the FixedUpdate() method. Methods like GetKeyDown() and other methods from the Input class which read keyboard/mouse/axis buttons/values should not be called during FixedUpdate(), as their returned values are unreliable.
Due to this, what is probably causing your jump implementation to fail is that the GetKeyDown() method you're calling in FixedUpdate() is returning inconsistent/invalid (false) results, when the user presses the jump key.
Fixing this can be quite simple. I suggest you keeping a boolean variable which keeps track of whether the jump key has been pressed, and gets its value updated during Update(). This should fix your problem.
bool jumpKeyPressed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
jumpKeyPressed = true;
else if (Input.GetKeyUp(KeyCode.Space))
jumpKeyPressed = false;
}
private void FixedUpdate()
{
/* Update "moveHorizontal", call HandleMovement(...) here, as you've already done. */
if (jumpKeyPressed && isJumping == false)
{
// IMPORTANT: this prevents the "jump force" from being applied multiple times, while the user holds the Space key
jumpKeyPressed = false;
/* Remaining jumping logic goes here (AddForce, set "isJumping", etc) */
}
}
It's because your logic for jumping is inside FixedUpdate()
When you use GetKeyDown to register input make sure to use Update instead because if you press the key using FixedUpdate it may or may not run during that frame, test it with Update instead.
You already have in comments how Update works, it is called every frame but FixedUpdate according to Unity documentation: This function is called every fixed framerate frame
void Update ()
{
float moveHorizontal = Input.GetAxis("Horizontal");
HandleMovement(moveHorizontal);
if (Input.GetKeyDown(KeyCode.Space) && isJumping == false)//by typing Space player jumps, cant double-jump
{
rgdb2.AddForce(new Vector2(rgdb2.velocity.x, 1 * jumpHeight), ForceMode2D.Impulse);
isJumping = true;
Debug.Log("jumped");
}
}
I've been working on this script for the past day. For some reason my character will not jump as long as it's animator is active. I've got into the animation (there is only one) and removed all references to the animation placing a position anywhere and still the issue presides.
I have discovered that I can make my player jump if I use Co-routine which I'm using. However, I'm still new to using them and I can't work out why my player won't fall to the ground once a force has been added to it. And my player only moves up when the button is clicked. Could someone please take a look at my script and tell me what I'm doing wrong?
public float jumpSpeed = 100.0f;
public float jumpHeight = 2.0f;
public AudioClip jumpSound;
private GameObject pos;
private bool moving;
private bool isJumping;
void Start()
{
}
// Update is called once per frame
void Update ()
{
if(Input.GetMouseButtonDown(0))// && !moving)
{
isJumping = true;
StartCoroutine(JumpPlayer(gameObject.transform.localPosition));
}
else
{
isJumping = false;
}
}
IEnumerator JumpPlayer(Vector3 startPos)
{
Vector3 jump = new Vector3(transform.localPosition.x, jumpHeight, transform.localPosition.z);
float t = 0f;
t += Time.deltaTime / jumpSpeed;
rigidbody.AddForce(Vector3.up * jumpSpeed);
//gameObject.transform.localPosition = Vector3.Lerp(startPos, jump, 0.5f);
//isJumping = false;
yield return null;
}
Firstly, your use of coroutine isn't doing anything in particular - because it only does yield return null at the end, it'll run in a single frame and then exit. You could make it a regular void function and you shouldn't see any change in behaviour.
Removing other redundant code and you have just this:
if(Input.GetMouseButtonDown(0))
{
rigidbody.AddForce(Vector3.up * jumpSpeed);
}
This force is added for only a single frame: the frame where the mouse button is pressed down (if you used Input.GetMouseButton instead, you'd see the force applied for multiple frames).
You say "my player only moves up when the button is clicked" but I'm not clear why that's a problem - perhaps you mean that the player should continue to move up for as long as the button is held, in which case you should refer to my previous paragraph.
The most obvious reasons for the player not falling again are related to the RigidBody component: do you have weight & drag set to suitable values? An easy way to test this would be to position your player some distance from the ground at the start of the scene, and ensure that they fall to the ground when you start the scene.
Another reason might be that you're using the default override of .AddForce in an Update cycle. The default behaviour of this method applies force during the FixedUpdate calls, and you might find that using ForceMode.Impulse or ForceMode.VelocityChange gives you the result you're looking for.
I'm writing a 2D game and I'm trying to get moving platforms to work. After doing some previous investigation, I have it ALMOST working. The idea is to have 2 platform objects with colliders: 1 a visible object, the other an invisible object with isTrigger set (since the player would just go through a trigger). The code for the Moving Platform child (the trigger one) is set here.
using UnityEngine;
using System.Collections;
public class MovingPlatformChild : MonoBehaviour
{
public string parentPlatform = "";
void Start ()
{
transform.parent = GameObject.Find(parentPlatform).transform;
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerEnter(Collider playerObject)
{
Debug.Log ("enter moving platform");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent = gameObject.transform;
}
}
int i = 0;
void OnTriggerStay(Collider playerObject)
{
Debug.Log ("stay" + i++);
if(playerObject.transform.position.y >= transform.position.y)
{
playerObject.transform.parent = gameObject.transform;
}
else
{
playerObject.transform.parent=null;
}
}
void OnTriggerExit(Collider playerObject)
{
Debug.Log ("EXIT");
if(playerObject.gameObject.name.Contains("Player"))
{
playerObject.transform.parent=null;
}
}
}
The Start() function just makes it a child of the visible platform. This can probably be done right in the Unity editor as well, instead of through code.
The OnTriggerEnter function adds the player object as a child of the trigger platform object, which is a child of the visible platform. So they should all move together.
The OnTriggerStay is an attempt to verify that this remains true only while the player is on the top of the platform. While the player is within the trigger, if the player is on top of the platform, then it remains attached. Otherwise, it's not. This is so that nothing happens on the bottom end.
The OnTriggerExit function just removes the player object as a child when it exits the trigger.
This is somewhat working (but we know somewhat isn't good enough). It works sometimes, but the player will be very jittery. Also, on the way down while standing atop the platform, the TriggerStay function doesn't appear to be called (implying the player is no longer within the trigger). This is observed through my Debug "stay" statement. Finally, sometimes the player will also fall straight through the platform.
What in this code would allow the player to fall through the platform, or be so jittery on the way up? Am I missing something crucial? If you need any more code, please let me know.
Below is the code for the movement of the non-trigger platform (the parent of the trigger platform and in an identical position). I will also share the Player's Update function after that.
void Start ()
{
origY = transform.position.y;
useSpeed = -directionSpeed;
}
// Update is called once per frame
void Update ()
{
if(origY - transform.position.y > distance)
{
useSpeed = directionSpeed; //flip direction
}
else if(origY - transform.position.y < -distance)
{
useSpeed = -directionSpeed; //flip direction
}
transform.Translate(0,useSpeed*Time.deltaTime,0);
}
And now the player code:
void Update()
{
CharacterController controller = GetComponent<CharacterController>();
float rotation = Input.GetAxis("Horizontal");
if(controller.isGrounded)
{
moveDirection.Set(rotation, 0, 0); //moveDirection = new Vector3(rotation, 0, 0);
moveDirection = transform.TransformDirection(moveDirection);
//running code
if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) //check if shift is held
{ running = true; }
else
{ running = false; }
moveDirection *= running ? runningSpeed : walkingSpeed; //set speed
//jump code
if(Input.GetButtonDown("Jump"))
{
//moveDirection.y = jumpHeight;
jump ();
}
}
moveDirection.y -= gravity * Time.deltaTime;
controller.Move(moveDirection * Time.deltaTime);
}
EDIT: I've added the specifications for the platforms and player in this imgur album:
http://imgur.com/a/IxgyS
This largely depends on the height of your trigger box, but it's worth looking into. Within your TriggerStay, you've got an IF statement concerning the player y coordinates. If the trigger box is fairly large and the platform's speed fast enough, on the way up and between update ticks the player Y coords could momentarily be smaller than the trigger Y coords. This would lead to him losing the parentage, only to regain it a few ticks later. This might be the cause of the 'jittering'.
The problem I was having included
The moving platform was written using Translate. I rewrote it using a rigidbody and the rigidbody.Move function. This didn't immediately help, but...
I realized the CharacterMotor script (Unity provides this) that I had attached to the player included moving platform support. I set the MovementTransfer value to PermaLocked, and also unchecked the "Use FixedUpdate" box on the script, and it now works 99% of the time. I've had one time where I did a particular behaviour and slipped through, but I can't recreate it.
Hope this helps anyone who might be looking for an answer!