Unity 2D C# - Collider not working - c#

I am working on a 2D TopDown game in Unity 5. The collision is not working at all.
The player and the obstacle both have a 2D Collider and they are not Trigger. The player has a 2D Rigidbody with Kinematic set to false, gravity scale is equal to zero. Maybe the movement code has something to do with it.
The code is a bit long so I'll just show you the code for moving up:
if (Input.GetAxis ("Up") > 0) {
if (movingDown == false) {
posY += speed * Time.deltaTime;
movingUp = true;
}
} else {
movingUp = false;
}
/.../
transform.position = new Vector3 (posX, posY, 0);
It is always setting the value of the position as long as you are pressing the up button. Is there a way to fix this?

I think that the problem is that you are setting the position directly. So at each frame, you are telling unity exactly were the object should be, which overrides the position that would be computed from collision.
To fix this, you need to modify your movement code to add a force to your rigidbody and leave the position untouched ( see rigidbody doc, and function AddForce (https://docs.unity3d.com/ScriptReference/Rigidbody.html)

Try using
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
This replaces your system with a velocity-based system as updating the transform.postition of a rigidbody is not recommended. With the system you have, the collision is not being detected because the rigidbody isn't being updated.

Related

Issue with Player Movement Unity

I am VERY new to unity and I wrote this very basic program to move the player. This program works when there is no conditional statement on if the w key is pressed, but if you add it, the capsule is unable to move.
if (Input.GetKeyDown("w"))
{
Vector3 cameraForward = Camera.main.transform.forward;
cameraForward.y = 0;
cameraForward = cameraForward.normalized;
Vector3 moveDirection = cameraForward * speed * Time.deltaTime;
transform.position += moveDirection;
}
The only issue I can see with this is that the capsule is clipping into the plain, although I have a collider and a rigidbody on it. If you run this the capsule just does not move, at all. If it at all matters, I also have another line of code that sets the rotation of the capsule to 0, 0, 0, 0 every frame.
Input.GetKeyDown() only triggers once per key-press. Your biggest issue (there may be others) is that that will only move the player forward for a single tick. You want Input.GetKey() (Unity Docs) instead.

Unity - Gravity not working when opening from another scene

I am creating a simple mini-game in unity as a separate scene. The problem is that the gravity works completely fine on my rigidbody when I start from the mini-game scene, but when I move to the mini-game scene from another the gravity stops working and my player can only go up.
Here is what I've tried:
My Project's Physics 2D settings are fine (Gravity: X = 0, Y = -9.81)
My Project's Time setting is fine (Time Scale: 1)
My Player character has a Rigidbody2D, which is Dynamic, Simulated (true), Gravity Scale = 5, ONLY rotation is frozen, and it's set to never sleep.
The Player also has box and circle colliders (both 2D).
All player inputs are read in Update(), and all physics are done in FixedUpdate() (I am using velocity to move the player).
I do have a GameController object which is carried between the scenes, but none of it's scripts should affect the scene (I can provide the OnLevelWasLoaded() parts of the scripts or any other parts).
Here is the code in my player controller:
void Start()
{
rb = GetComponent<Rigidbody2D>();
box = GetComponent<BoxCollider2D>();
_renderer = GetComponent<SpriteRenderer>();
velVer = 30f;
velHor = 10f;
}
void Update()
{
moveHor = Input.GetAxisRaw("Horizontal");
moveVer = Input.GetAxis("Vertical");
buttonUp = Input.GetButtonUp("Vertical");
_moveVector = new Vector2(moveHor, moveVer);
}
void FixedUpdate()
{
if (IsGrounded() && buttonUp)
rb.velocity = new Vector2(velHor * _moveVector.x, velVer * _moveVector.y);
if (_moveVector.x != 0 && _moveVector.y == 0)
rb.velocity = new Vector2(velHor * _moveVector.x, rb.velocity.y);
}
private bool IsGrounded()
{
RaycastHit2D raycast = Physics2D.BoxCast(box.bounds.center, box.bounds.size, 0f, Vector2.down, 0.1f, layer);
return raycast.collider != null;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag.Equals("Heart"))
{
other.gameObject.SetActive(false);
}
}
I have commented out any references to the GameController object from all scripts in the mini-game scene, so the GameController should not have any impact on my mini-game character controller.
I am desperate, I have searched online for more than 3 hours and haven't found anything that could help me (if I did actually miss a solution my apologies).
I can provide any further information you may require.
EDIT:
Here are Gifs of character behaviour when I start the game from the mini-game scene, and when I move to the mini-game scene from another.
Working (started mini-game directly):
Not Working (moved to mini-game from another scene):
Check for three things:
1) Are you sure that you didn't set timeScale to 0 in any other script?
2) Does your player have the same scale on both levels?
3) Why do you use Physics2D.BoxCast in IsGrounded function? I usually use Raycast2D for the ground checking process.
Okay I have found the issue! I thought the problem would lie inside the scripts that are inside of my mini-game scene, or in the scripts carried into the scene by my GameController, but that was not the case. The problem laid in a script attached to an object located in a scene from which I moved to the mini-game.
Here if the troublesome line:
Physics2D.gravity = Vector2.zero;
Because my game is 2D with Axonometric projection (3/4) I set the gravity to Vector2.zero very early into the development. I have replaced that with simply setting the gravity scale to 0.
rb.gravityScale = 0;
Even though the script with the troublesome line doesn't move to my mini-game scene it modified the gravity setting of the entire project.
VERY silly mistake, which I probably would find, but after many hours of constant development my brain wasn't functioning too well, and I decided to post this question.
Thanks for the help anyway guys!

Instantiate a prefab pointing in the direction of another object

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(....) { .... }

Rigidbody Mobs fall through ground while walking

I have a really odd issue, I created custom MOB AI in unity for my game (it has procedural generated voxel world so agents didn't work well). I use rigidbody on the mobs which I move around.
But I have this issue where mobs go inside the floor while moving (doesn't happen when standing) and when they stand, they teleport back up!. It's not animation, I disabled animations and it still happens.
here is how I move them:
private void WalkToTarget()
{
if (goal != Vector3.zero)
{
if (goal.y <= 5)
{
currentstatus = MOBSTATUS.STANDING;
return;
}
var distance = Vector3.Distance(VoxelPlayPlayer.instance.transform.position, gameObject.transform.position);
if (distance < 15)
{
goal = VoxelPlayPlayer.instance.transform.position;
goal.y = VoxelPlayEnvironment.instance.GetHeight(goal);
}
if (distance < 5)
{
this.currentstatus = MOBSTATUS.ATTACKING;
}
//MOVEMENT HAPPENS HERE
Vector3 direction = (goal - mobcontroller.transform.position).normalized*2f;
if(mobcontroller.collisionFlags!=CollisionFlags.CollidedBelow)
direction+= Vector3.down;
mobcontroller.Move(direction * Time.fixedDeltaTime);
RotateTowards(direction);
}
}
Edit:
All code: https://pastebin.com/khCmfKGi
Part of your problem is that you are using CollisionFlags incorrectly.
Instead of this:
if(mobcontroller.collisionFlags!=CollisionFlags.CollidedBelow)
You need to do this
if(mobcontroller.collisionFlags & CollisionFlags.CollidedBelow)
Because you are trying to check if the mob is at least colliding below, not if the mob is only colliding below.
Even then, CharacterController.Move should not move you through colliders on its own.
I suspect that RotateTowards(direction) might be rotating the boundaries of mob's collider through the ground in some cases. To prevent that, I recommend creating a lookDirection that keeps the character rotation flat when you do your RotateTowards:
Vector3 direction = (goal - mobcontroller.transform.position).normalized*2f;
if(mobcontroller.collisionFlags & CollisionFlags.CollidedBelow)
direction+= Vector3.down;
mobcontroller.Move(direction * Time.fixedDeltaTime);
Vector3 lookDirection = (goal - mobController.transform.position);
lookDirection.y = mobController.transform.y;
RotateTowards(lookDirection);
This problem happened when using Rigidbody and CharacterController on the mob. Removing Rigidbody from the mob solved this problem.

Air hockey game - Player bat goes through puck if moved too fast

Im currently developing an Air hockey game in Unity3d. The issue I'm having is that when the player attempts to hit the puck too quickly, the player ends up going through the puck and therefore there is no collision. The game works perfectly as expected if the player stays still and the puck hits the player or if the player hits the puck at a slow pace.
The player has a rigidbody using continuous collision detection using a capsule collider. The puck also has rigidbody with continuous dynamic collision detection and a mesh collider with convex.
I tried setting the fixed timestep to 0.01 but that didn't have an effect. Here is the script for the player movement:
void ObjectFollowCursor()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 point = ray.origin + (ray.direction * distance);
Vector3 temp = point;
temp.y = 0.2f; // limits player on y axis
cursorObject.position = temp;
}
and here is the code for the puck when it collides with the player:
// If puck hits player
if(collision.gameObject.tag == "Player")
{
Vector3 forceVec = this.GetComponent<Rigidbody>().velocity.normalized * hitForce;
rb.AddForce(forceVec, ForceMode.Impulse);
Debug.Log ("Player Hit");
}
Any help would be much appreciated. Thanks.
The problem you are having its called "tunneling".
This happens because your object is moving at a high speed and in that specific frame the collision is not detected. In frame n the ball is just in front of the bat, but when frame n+1 is calculated the ball has moved behind the bat, thus "missing" the collision completely.
It is a common problem but there are solutions.
I recommend you study this script and try to implement on your game.
This is not my code:
SOURCE: http://wiki.unity3d.com/index.php?title=DontGoThroughThings
using UnityEngine;
using System.Collections;
public class DontGoThroughThings : MonoBehaviour
{
// Careful when setting this to true - it might cause double
// events to be fired - but it won't pass through the trigger
public bool sendTriggerMessage = false;
public LayerMask layerMask = -1; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Collider myCollider;
//initialize values
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
myCollider = GetComponent<Collider>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector3 movementThisStep = myRigidbody.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
RaycastHit hitInfo;
//check for obstructions we might have missed
if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movementMagnitude, layerMask.value))
{
if (!hitInfo.collider)
return;
if (hitInfo.collider.isTrigger)
hitInfo.collider.SendMessage("OnTriggerEnter", myCollider);
if (!hitInfo.collider.isTrigger)
myRigidbody.position = hitInfo.point - (movementThisStep / movementMagnitude) * partialExtent;
}
}
previousPosition = myRigidbody.position;
}
}
You were right to try continuous collision detection (CCD). There are some constraints (especially in this case where you want to use CCD with two moving objects rather than one moving object and one static object), but it is designed for this kind of scenario. The Rigidbody documentation goes into these constraints:
Set the collision detection mode to Continuous to prevent the
rigidbody from passing through any static (ie, non-rigidbody)
MeshColliders. Set it to Continuous Dynamic to also prevent the
rigidbody from passing through any other supported rigidbodies with
collision detection mode set to Continuous or Continuous Dynamic.
Continuous collision detection is supported for Box-, Sphere- and
CapsuleColliders.
To sum up, both puck and paddle need to be set to Continuous Dynamic, and both need to be Box-, Sphere-, or Capsule Colliders. If you can make these constraints work for your game you should be able to get continuous collision detection without writing it yourself.
A note about Unity's CCD that bears repeating:
Note that continuous collision detection is intended as a safety net
to catch collisions in cases where objects would otherwise pass
through each other, but will not deliver physically accurate collision
results, so you might still consider decreasing the fixed Time step
value in the TimeManager inspector to make the simulation more
precise, if you run into problems with fast moving objects.
But since you are manually specifying the collision reaction, that might not be an issue.

Categories