I attach a movement script to the player. with reference to the roller ball tutorial and modified it for jump. the problem is that when may player moves in any direction the player starts to rotate in that direction and even if i am standing still at the one position it starts to rotate and fall down the platform. the player has a rigidbody, boxcollider components.
void Awake ()
{
playerRigidbody = GetComponent<Rigidbody>();
Coll = GetComponent<CapsuleCollider>();
}
/*private void Update()
{
}*/
// Update is called once per frame
void FixedUpdate ()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool down = Input.GetKeyDown(KeyCode.Space);
if (down)
{
playerRigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Move(h, v);
}
private bool IsGrounded()
{
return Physics.CheckCapsule(Coll.bounds.center, new Vector3(Coll.bounds.center.x,Coll.bounds.min.y,Coll.bounds.center.z),Coll.height * 9f,Ground);
}
void Move (float h, float v)
{
movement.Set(h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
In your code,you use AddForce function,so I confirm your player rigidbody doesn't set Kinematic to true.
When your charactor collision with any other objects ,such as ground,wall, your charactor maybe rotate.
You could freeze rigidbody's rotation via inspector like Bijan says.
I don't recommand your use AddForce and MovePosition together.It may has other problem.
like this
This is due to the nature of the Rigidbody component. This component is very lifelike and when force is added and removed, the object still has inertia and therefore the object is still moving.
When it comes to the rotation it is due to the fact that when a force is applied to an object, the object also acquires rotational motion (also known as angular motion).
You can freeze the rotation using the constraints on the Rigidbody component.
Have a deeper look into this component [here].1
In my opinion, it is one of the core components of Unity and it is definitely worth looking into.
I hope I answered your question!
Related
So I am making a small game in unity where you have to shoot the enemy. However, when I made the script for the bullet and enemy, it half worked and half didn't. Sometimes, the bullet would hit the enemy and destroy the enemy, however, sometimes, it would take multiple shots for it to work. But when I turn the speed of the bullet to 1 (the speed of the bullet was 500), the bullet always destroys the enemy. So this leads me to think that this has something to do with the speed of the bullet. Here is my script
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
Destroy(other.gameObject);
Destroy(gameObject);
Debug.Log("e");
}
For the movement of the bullet, I just used transform.Translate(Vector3.up * Time.deltaTime * speed). How can I fix this?
The problem is not that Destroy do not work with a certain speed, the problem is that with certain speed you are not triggering the "OnTriggerEnter".
This fenomenon is called "tunneling" it happens when the object goes too fast.
That provokes that in one frame the projectile is on one side of the collider, and in the next frame is on the other side of the collider, giving the sensation like a teleport, so that's the why it do not collide, cause in any frame the engine has detected the collide.
If you're having troubles with high speed stuff, try to set your rigidbody (the one that is moving) to Interpolate, or use raycasts to fake bigger projectile colliders.
In addition to Lotan's answer
I just used transform.Translate(Vector3.up * Time.deltaTime * speed)
whenever using physics you do NOT want to set anything via Transform as this bypasses the physics engine and thereby breaks collision detection etc as in your case.
You rather want to through the Rigidbody component and e.g. use Rigidbody.MovePosition
Rigidbody.MovePosition moves a Rigidbody and complies with the interpolation settings
like
private Rigidbody _rigidbody;
private void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.rotation * Vector3.up * Time.deltaTime * speed);
}
or why not simply set it only once
private void Start()
{
GetComponent<Rigidbody>().velocity = transform.up * speed;
}
Additionally you want to set the interpolation to dynamic.
A tricky way : bullet form A point to B point by one frame , so you can fake a point on a to b like
fake point: A+B/2
Point a ;
Point b ;
Update()
{
Point ab = (a+b)/2;
bullet.point = ab;
//check collider some .
bullet.point = b; // set it back.
a = b;
}
Not a good solution . but it have double hit rate.
The Issue
I'm making a 2D Unity game where the main weapon of your character is a fireball gun. The idea is that a fireball will shoot out of the player's hand at the same angle the player's hand is pointing. I have 3 issues:
When I shoot the fireball, since the fireball is a RididBody, it pushes the player. This is because I've made the centre of the player's arm (the same place where the fireball shoots from) the point at which the arm rotates around the player (what is meant to be the shoulder);
To instantiate the fireball prefab on the arm, the only way I know how to do it is by using a piece of code which requires the arm to be a RigidBody. This means that the arm is affected by gravity and falls off the player on start unless I freeze the arm's y-axis movement, which means that when the player jumps, while the arm does not fall, it floats at the same y-position as where it started while moving along the x-axis; and
When the fireball is shot, the angle from which it is propelled after being shot is not the same angle as the angle of the player's arm.
Instantiating the Fireball
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var fireballTransform = Instantiate(fireballPrefab); //creates a new shot sprite
fireballTransform.position = new Vector3(transform.position.x + horizMultiplier, transform.position.y, transform.position.z);
fireballTransform.rotation = orientation;
fireballTransform.transform.Rotate(0, 0, transform.rotation.z);
}
if (Input.GetKeyDown(KeyCode.D)) // moves right
{
orientation = 0;
horizMultiplier = 0.08F;
}
if (Input.GetKeyDown(KeyCode.A)) // moves left
{
orientation = 180;
horizMultiplier = -0.08F;
}
This piece of code is located within the script applied to the player's arm. The movement of the arm works fine and the problem seems to be either within this piece of code or the code for my fireball (which I will put next). A few definitions:
pew is a sound effect played when the fireball is shot;
horizMultiplier is the distance from the arm's centre which I would like the fireball to instantiate (also dependant of if the player) is facing left or right); and
orientation is which direction the player is facing (left or right). The fireball is then instantiated facing that same direction.
Fireball Script
public Vector2 speed = new Vector2(); // x and y forces respectively
private Rigidbody2D rb; // shorthand
private float rotation;
void Start()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rotation = rb.rotation;
if (rotation == 0)
{
rb.AddForce(Vector3.right * speed.x); // propels right
}
if (rotation == 180)
{
rb.AddForce(Vector3.left * speed.x); // propels left
}
}
I believe this code is explanatory enough with comments (if not please comment and I'll address any question). I believe an issue could also be in this piece of code because of the lines: rb.AddForce(Vector3.right * speed.x); and rb.AddForce(Vector3.left * speed.x); as these add directional forces to the object. I don't know is this is objective direction (right or left no matter what direction the object the force is being applied to is facing) or if it's right or left in terms of the object-- say if an object was rotated 90 degrees clockwise and that object had a force applied so that it moves right making the object move downwards.
What I'm expecting to happen is the player's arm will turn so that when a fireball is fired it is fired in the direction the arm is facing. The arms turning mechanics are fine, it's just trying to properly instantiate the fireball. Can anyone help with any of the issues I've laid out?
How I would go about this:
Add an empty transform as child of the arm and move it to where the fireball should spawn. Also make sure the rotation of it is such that its forward vector (the local z-axis) points to where the fireball should go. To see this, you need to set "Local" left of the play button.
Spawn the fireball at the transform's position with its rotation.
Ignore collision between the arm's/player's collider and the fireball's collider with https://docs.unity3d.com/ScriptReference/Physics.IgnoreCollision.html. If necessary, you can enable the collision between the two colliders again after 1s or so.
Set the fireball rigidbody's velocity to speed * rigidbody.forward.
If you need help with the code, please post it so I can see what's going on.
Edit:
The arm definitely doesn't require a Rigidbody.
You can just use Instantiate(prefab, position, rotation) as shorthand.
Is this for a 2D game?
Also, I'm going to sleep now but I'll gladly try to help tomorrow.
Your Issues
1) You should be masking the fireball's layer not to collide with your player's layer.
You can find more info about this here: https://docs.unity3d.com/Manual/LayerBasedCollision.html
(note: make sure you're on the Physics2D panel, and not the Physics one, as that's for 3D)
2) There is a setting called gravityScale in RigidBody2D. You should set this to 0 if you don't want gravity to be affecting your object. More info: https://docs.unity3d.com/Manual/class-Rigidbody2D.html
Fireball Instantiate
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var position = hand.transform.position + hand.transform.forward * horizMultiplier;
var rotation = hand.transform.rotation;
var fireball = Instantiate<Fireball>(fireballPrefab, position, rotation);
fireball.StartMoving();
}
Fireball Script
private Rigidbody2D rb; // shorthand
private float rotation;
public float Speed;
public void StartMoving()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rb.velocity = transform.forward * Speed;
}
void OnTriggerEnter(....) { .... }
When I set the speed of Ball = 10 OnTriggerEnter2D to test the ball hit on the floor work fine, but when I set the speed higher (20), OnTriggerEnter2D doesn't work and the ball falls down through the floor
My code:
void Start () {
rigiBody = GetComponent<Rigidbody2D>();
ballLayer = 1 << LayerMask.NameToLayer("Ball");
}
void OnTriggerEnter2D(Collider2D other) {
if (other.CompareTag(Constants.FLOOR_TAG))
{
Debug.Log("FLOOR_TAG");
if (HitFloor != null)
HitFloor(this);
}
}
void FixedUpdate() {
Vector2 tempVect = Direction;
tempVect = tempVect.normalized * Speed * Time.deltaTime;
Vector2 newPos = rigiBody.position + tempVect;
rigiBody.MovePosition(newPos);
timer += Time.deltaTime;
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
if (!hit)
return;
...
Inspector of Ball below
What's wrong with this code?
ps I'm using Unity 2017.1.1f1 Personal
You have to change the "Collision Detection" property of rigidbody. It should be "continuous" not "discrete". If you choose discrete value you are telling rigidbody to check collision in discrete time intervals. If you move in a high speed, rigidbody will probably miss collision.
Set the collision detection mode in the Rigidbody2D component to Continuous. Documentation
And maybe changing
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
to
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * 1.2f, ~(ballLayer));
will fix the problem aswell.
Why do you cast the Ray from the newPosition? Imo. You should cast it from its current Position.
Solution of my problem was really close
A couple of lines were added and changed
add [RequireComponent(typeof(Rigidbody2D))] for class Ball
replace variable RigiBody to property
Now everything work well, and balls not fall through the floor on high speed
Code after changes look like this
[RequireComponent(typeof(Rigidbody2D))] // Added this code
public class Ball : MonoBehaviour {
private Rigidbody2D _rigiBody;
public Rigidbody2D RigidBody { //And this property
get {
if (_rigiBody == null)
_rigiBody = GetComponent<Rigidbody2D>();
return _rigiBody;
}
}
I got a "cube" in Unity. This cube has a Trigger, when moving into it, the object gets grabbed into the air. This is working as a lift, you can see an example what I mean here. It is a small scene taken from Spyro 2.
https://youtu.be/f8wWMa4N5mE?t=643
The code I use is really small for now
private float liftSpeed = 10; // the speed, the object is flying up
private void OnTriggerStay(Collider col)
{
Rigidbody objectRigid = col.gameObject.GetComponent<Rigidbody>(); // get the rigidbody from the object in the trigger
if (objectRigid != null) // does it have a rigidbody?
objectRigid.velocity = new Vector3(objectRigid.velocity.x, liftSpeed, objectRigid.velocity.z); // make it fly in the air
}
So I have a lift, that totally works fine. But I when I rotate my lift I want it to work aswell.
Some examples (my gamne is 3D)
So how can I make my lift work for all "rotations"?
You can use transform.up to get the up direction of your lift, and then multiply by the lift speed.
objectRigid.velocity = transform.up * liftSpeed;
transform.up changes depending on how the object is rotated, so if your lift is rotated to the left, then the lift will carry objects to the left.
You can use the transforms RotateAround Method for a rotation like in the video clip.
Transform t = col.gameObject.GetComponent<Transform>();
transform.RotateAround(Vector3.zero, Vector3.up, 20 * Time.deltaTime);
The given snipet lets the object rotate around it's axis, which is pointing up.
Make the player child of the lift OnTriggerEnter.
Move the lift as you do
You can rotate the lift using RotateAround function. The player should also rotate along with the lift, as it's a child of the lift now.
make player's parent to null when it's out of lift trigger using OnTriggerExit
This is my solution to the problem.
The lift and the EndPoint will be Empty GameObjects. If you want the lift has a platform or a base you can add a GameObject as child of the lift with the shape you prefer.
You need to attach a Collider to the lift and set it as trigger.
Then you add the following script to the lift Empty GameObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLift : MonoBehaviour {
public Transform end;
float speed = 4f;
bool liftActivated = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(liftActivated)
{
float step = speed * Time.deltaTime;
//To move up the lift
transform.position = Vector3.MoveTowards(transform.position, end.position, step);
//To spin the lift
transform.RotateAround(Vector3.up, 2 * Time.deltaTime);
//To stop spining the lift when it reaches the end of the path
if(transform.position.Equals(end.position))
liftActivated = false;
}
}
void OnTriggerEnter(Collider other){
liftActivated = true;
other.gameObject.transform.parent = this.transform;
other.gameObject.GetComponent<Rigidbody>().isKinematic=true;
}
}
Then you should decide if once the lift reaches its destination it is the player who has to move out of the platform, or if you unparent it and let it fall down as the end of the trip
I have a blockbreaker type game coded using C# that mainly works fine but upon play testing i have found the ball will slow right down in certain situations! i.e if it wedges between to game object bricks the force of the ball rapidly slows down! 8 times out of ten this will not happen but other times it does and im unsure why! i will post the Ball script i think you will need to help solve this but should you need anymore information then please ask.
public class Ball : MonoBehaviour {
private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
void Start () {
paddle = GameObject.FindObjectOfType<Paddle> ();
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if (!hasStarted) {
//lock ball relative to the paddle
this.transform.position = paddle.transform.position + paddleToBallVector;
//wait for mouse press to start
if (Input.GetMouseButtonDown (0)) {
//if (Input.GetTouch(0).phase == TouchPhase.Ended){
hasStarted = true;
this.GetComponent<Rigidbody2D> ().velocity = new Vector2 (2f, 10f);
}
}
}
void OnCollisionEnter2D(Collision2D collision){
Vector2 tweak = new Vector2 (Random.Range(0f,0.2f),Random.Range(0f,0.2f));
if (hasStarted) {
GetComponent<AudioSource> ().Play ();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
}
You are adding directly to the velocity of the ball. The velocity variable defines a direction and speed, not just a speed as you are thinking here.
So, when the ball collides with a block it has a velocity of (+x, +y). After the collision and bounce is has a velocity of (+x, -y). So by adding a random positive value to the velocity when it has the -y velocity means that it will slow down the ball.
This does not happen every time because you are moving the ball in your Update() method. Change that to 'FixedUpdated()'. Fixed Update handles all physics calculations.
Also, I would recommend having a RigidBody2D on your Ball object. Moving physics objects should always have RigidBodies. With this, you can then use AddForce in the forward direction of your ball and that should solve your problem.
EDIT: I see you have a RigidBody2D already. I missed that the first time. Instead of having GetComponent<RigidBody2D>().velocity try GetComponent<RigidBody2D>().AddForce( transform.forward * impulseAmount, ForceMode.Impluse );