I have a simple scene in 2D. The right yellow box is the "Player", while the green & brown thing is the "Obstacle".
Player has a BoxCollider2D, RigidBody2D and a C# script named Hero.cs attached to it. BoxCollider2D enabled Is Trigger; RigidBody2D enabled Is Kinematics; other settings are left in default values.
Obstacle has only a BoxCollider2D with Is Trigger enabled.
and here is the Hero.cs:
using UnityEngine;
using System.Collections;
public class Hero : MonoBehaviour {
public float moveSpeed = 0.1f;
private Vector3 moveDirection;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Vector3 currentPos = transform.position;
if(Input.GetKey("left")) {
transform.Translate(new Vector3(-1, 0, 0));
} else if(Input.GetKey("right")) {
transform.Translate(new Vector3(1, 0, 0));
}
}
void OnCollisionEnter2D(Collision2D collision) {
Debug.Log("Colliding");
}
void OnTriggerEnter2D(Collider2D other) {
Debug.Log("Triggering");
}
}
Only "Triggering" appears in Console Log.
My question is: What should I add to make the "Player" inaccessible to the "Obstacle" (no need to bounce away)?
Note: Using Unity 4.5
Update: After I set Gravity Scale to 0, collision detection works, but in a strange way. The "Player" go sideway during collision. Watch this YouTube video for action.
I expect the Player only go along X or Y axis. What did I miss ?
IsTrigger
Triggers let other colliders pass through without any collision happening. They are only triggering an event, hence the name.
If you disable IsTrigger for both objects, you will get collisions and the corresponding events are fired.
More infos here: http://docs.unity3d.com/Manual/CollidersOverview.html
IsKinematic
Kinematic rigidbody colliders will only collide with other non-kinematic rigidbody colliders.
Have a look at this matrix http://docs.unity3d.com/Manual/CollisionsOverview.html
Disable IsKinematic and move the player with MovePosition if you don't want to use force values to move the player.
Related
I have a moving platform in a 2D Sidescroller built in Unity 2020.1
The Moving Platform translates between two points using the MoveTo method. It does not have a RigidBody2D component.
I attach the Player to the platform by making it the child of the platform using OnCollisionEnter2D and OnCollisionExit2D to parent the Player to the parent and reset to null respectively. Works great.
I'm using the CharacterController from Standard Assets.
The problem:
The player just walks in place when I try to move him back and forth on the platform.
What I've tried so far:
Changing the current velocity of the player by adding a constant to the x dimension of it's move vector.
Works kinda sorta but that constant needs to be huge to get it to move even a little bit. It's a huge kluge that violates every sense of coding propriety.
Put a RigidBody2D on the platform. Make it kinematic so it doesn't fall to the ground when I land on it. Move the platform via "rb.velocity = new Vector2(speed, rb.velocity.y)";
2a) Attempt to make the Player a child of the kinematic platform.
Player is made a child, but it doesn't move with the platform as expected. I believe that this is because both objects have RigidBody2D components, which I gather don't play well together based on what I've read.
2b) Attempt to add the platform's moving vector to the player's movement vector to make him stay in one place. Player stays stationary to make sure he stays fixed on the platform.
No dice.
I'm all out of ideas. Perusing videos on making player's stick to moving platforms all use the platform to move the player from place to place, without expecting that the game may want the player to move back and forth on the platform as the platform is moving.
I can't believe that this isn't a solved problem, but my Google foo isn't getting me any answers.
Thanks.
I'm a fairly newbie to Unity and C# but I wanted to help so I tried simulating your game for a solution and I didn't run into any problems using this script as the Player movement (you can modify variables as u like, add a separate variable for jump speed to make it smoother etc)
public class Player : MonoBehaviour {
Rigidbody2D rb;
float speed = 7f;
Vector3 movement;
public bool isOnGround;
public bool isOnPlatform;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * speed * Time.deltaTime;
Jump();
}
void Jump()
{
if (Input.GetButtonDown("Jump") && isOnGround || Input.GetButtonDown("Jump") && isOnPlatform)
{
rb.AddForce(transform.up * speed, ForceMode2D.Impulse);
}
}
}
Also add an empty child object to your Player gameObject and add a BoxCollider2D at his feet, narrow it down on Y axis like this
also attach this script to that child gameObject to check if player is on the ground(tag ground collider objects with new tag "Ground") so u don't jump infinitely while in the air OR if the player is on the platform(tag platform collider objects with "Platform") so you're still able to jump off it
public class GroundCheck : MonoBehaviour {
Player player;
MovingPlatform mp;
// Start is called before the first frame update
void Start()
{
player = FindObjectOfType<Player>();
mp = FindObjectOfType<MovingPlatform>();
}
private void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.tag == "Ground")
{
player.isOnGround = true;
}
if (other.gameObject.tag == "Platform")
{
player.isOnPlatform = true;
transform.parent.SetParent(other.transform);
mp.MoveThePlatform();
}
}
private void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.tag == "Ground")
{
player.isOnGround = false;
}
if (other.gameObject.tag == "Platform")
{
transform.parent.SetParent(null);
}
}
}
and finally for platform movement (no RigidBody2Ds, just a collider)
public class MovingPlatform : MonoBehaviour {
bool moving;
public Transform moveHere;
// Update is called once per frame
void Update()
{
if (moving)
{
gameObject.transform.position = Vector2.MoveTowards(transform.position, moveHere.position, 2f * Time.deltaTime);
}
}
public void MoveThePlatform()
{
moving = true;
}
}
additional images
Player, Platform
P.s. Forgot to add - on Player's RigidBody2D, under Constraints, check the "Freeze Rotation Z" box.
I have a rectangle player sprite with a Box Collider 2D and a Rigidbody2D attached. I also have a script for point-and-click movement attached to the player object (i.e. player moves to mouse click position). However as soon as the player character hits a collider, it starts to jitter rather than just fully stop. I don't know a lot about Unity physics other than what I've picked up in a few tutorials, so I'll include as much relevant information as I can.
The Rigidbody 2D component has all forces set to 0, except for mass being 0.0001. The body type is dynamic, and collision detection is set to continuous. My movement script looks like this, got it straight from a tutorial:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControls : MonoBehaviour
{
public float speed = 1;
private Vector3 target;
void Start()
{
target = transform.position;
}
void Update()
{
if (Input.GetMouseButtonDown(0)) {
target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
target.z = transform.position.z;
}
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
Is there an easier way to implement smooth point-and-click movement?
I have made a Raycast that goes from my camera to the point of the object clicked. However, I am trying to make an object (in this case a bullet) to fly along the path of the ray. At the moment it flies straight forwards from the camera no matter where on the object you click because of the vector 3. How would I get it to follow the Ray?
C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastShot : MonoBehaviour {
public Camera camera;
private Ray ray;
private RaycastHit hit;
public GameObject bullet;
private GameObject createBullet;
private Collider collider;
void Update () {
if (Input.GetMouseButtonDown (0)) {
ray = camera.ScreenPointToRay (Input.mousePosition);
createBullet = Instantiate (bullet, camera.transform.position, bullet.transform.rotation);
createBullet.AddComponent<Rigidbody>();
createBullet.GetComponent<Rigidbody>().AddRelativeForce (new Vector3(0, 1500, 0));
createBullet.GetComponent<Rigidbody>().useGravity = false;
collider = createBullet.GetComponent<Collider> ();
Destroy (collider);
if (Physics.Raycast (ray, out hit)) {
}
}
Debug.DrawLine (ray.origin, hit.point, Color.red);
}
}
You would want to use ray.direction property instead of (0,1500,0) as the direction of the force.
The add force should occur in FixedUpdate, and should only occur if the Ray hits something. Where you have it now is probably not the best spot.
Of course, make sure the bullet gets instantiated at the camera's location first.
Ray.direction gives you the vector3 direction of the ray object. If you need the distance at which it hit, you could also use ray.distance.
Edit: I'm near my computer now, so here's a more detailed answer relating to your comments.
First off: Here's the way I set up the test Project:
I Created a prefab bullet. This is just a sphere with a rigidbody, with my "BulletController" script attached to it. The point of prefabs is to avoid all of those lines where you have to add components. For testing purposes I set the rigibody to ignore gravity and its mass to 0.1.
Next, I created the BulletController script, which will be attached to the bullet prefab.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletController : MonoBehaviour {
Rigidbody rb;
public float bulletForce;
bool firstTime = false;
Vector3 direction;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody> ();
}
public void SetDirection (Vector3 dir) {
direction = dir;
firstTime = true;
}
void OnCollisionEnter () {
//code for when bullet hits something
}
void FixedUpdate () {
if (firstTime) {
rb.AddForce (direction * bulletForce);
firstTime = false;
}
}
}
This script is is charge of controlling bullets. The (later on) script that will create the bullets doesn't really care what happens to them afterwards, since its job is just to create bullets. This BulletController script is in charge of dealing with bullets once they're created.
The main parts are the SetDirection method which tells the bullet which direction to travel in. Also it adds a one-time force in its FixedUpdate method that pushes it in the direction you just set. FixedUpdate is used for physics changes like adding forces. Don't use Update to do this kind of thing. It multiplies the force by a force that you set called "bulletForce".
Finally the BulletListener Script, which is simply attached to an empty game object in the scene. This script is in charge of listening for mouse clicks and creating bullets towards them.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletListener : MonoBehaviour {
public Camera mainCamera;
public BulletController bulletPrefab;
void Update () {
if (Input.GetMouseButtonDown (0)) {
//create ray from camera to mousePosition
Ray ray = mainCamera.ScreenPointToRay (Input.mousePosition);
//Create bullet from the prefab
BulletController newBullet = Instantiate (bulletPrefab.gameObject).GetComponent<BulletController> ();
//Make the new bullet start at camera
newBullet.transform.position = mainCamera.transform.position;
//set bullet direction
newBullet.SetDirection (ray.direction);
}
}
}
In the inspector for this empty game object, I added this script, and then dragged the camera, and the bulletPrefab into the appropriate fields. Be sure to drag the prefab from the FOLDER, not from the SCENE. Since this will use the prefab, not an object in the scene.
Now click around and you'll see the bullets flying! Note that using a low force is good to test, and then increase it later.
The main things to take away from this is to split up your logic. A script should only be in charge of one thing. For example, your enemies might also fire bullets. You can now reuse your bulletController script for those bullets as well. Also, say you have different sized or shaped bullets, you can just drag the bulletcontroller script onto the different prefabs you've made for your bullets. This will not affect your listener script which will still create bullets where you click.
If you have the end point then you can move along the vector with MoveTowards:
Vector3 target = hit.point;
StartCoroutine(MoveAlong(target));
private IEnumerator MoveAlong(Vector3 target){
while(this.transform.position != target){
this.transform.position = MoveTowards(this.transform.position, target, step);
yield return null;
}
}
i am bloody beginner with Unity and i am currently working on a 2D Brawler. The movement works perfectly but my colliders don't do what they should... I want to detect if two GameObjects Collide (Spear and Player2) and if the collide Player2s healthPoints should decrease by Spears AttackDamage.
The names of the GameObjects are also their tags. The Spears Prefab has following configuration: SpriteRendered(Material Sprites-Default), BoxCollider2D(Material None Physics Material 2D, IsTrigger(not activated), UsedByEffector(also not activated) Rigidbody2D(Kinematic, None Material, Simulated(Activated), KinematicContacts(activated), Standard configs for the rest))
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpearCtr : MonoBehaviour {
public Vector2 speed;
public float delay;
Rigidbody2D rb;
void Start ()
{
rb = GetComponent<Rigidbody2D>();
rb.velocity = speed;
Destroy(gameObject, delay);
}
void Update ()
{
rb.velocity = speed;
}
}
The Players Configurations
The Spears Configurations
This was the code i have tried before
OnCollision2D(Collision2D target);
{
if (target.gameObject.tag == "Spear")
{
hp = -1;
if (hp <= 0)
{
alive = false;
}
}
}
I hope someone can tell me how to get this working
Thanks for all the answers
(BTW sorry for my bad english I am austrian)
enter image description here
enter image description here
Reasons why OnCollisionEnter() does not work:
Collison:
1.Rigidbody or Rigidbody2D is not attached.
At-least, one of the two GameObjects must have Rigidbody attached to it if it is a 3D GameObject. Rigidbody2D should be attached if it is a 2D GameObject/2D Collider.
2.Incorrect Spelling
You failed to spell it right. Its spelling is also case sensitive.
The correct Spellings:
For 3D MeshRenderer/Collider:
OnCollisionEnter
OnCollisionStay
OnCollisionExit
For 2D SpriteRenderer/Collider2D:
OnCollisionEnter2D
OnCollisionStay2D
OnCollisionExit2D
3.Collider has IsTrigger checked. Uncheck this for the OnCollisionXXX functions to be called.
4.The script is not attached to any of the Colliding GameObjects. Attach the script to the GameObject.
5.You provided the wrong parameter to the callback functions.
For 3D MeshRenderer/Collider:
The parameter is Collision not Collider.
It is:
void OnCollisionEnter(Collision collision) {}
not
void OnCollisionEnter(Collider collision) {}
For 2D SpriteRenderer/Collider2D:
6.Both Rigidbody that collides has a isKinematic enabled. The callback function will not be called in this case.
This is the complete collison table:
I started making a simple game in Unity3d: a tank to shoot at a wall (see image).
A GameObject is attached to the turret of the tank, and to this GameObject is attached the following script :
using UnityEngine;
using System.Collections;
public class Shooter : MonoBehaviour {
public Rigidbody bullet;
public float power = 1500f;
void Update () {
if (Input.GetButtonDown ("Fire1")) {
Rigidbody bulletRB = Instantiate (bullet, transform.position, transform.rotation) as Rigidbody;
Vector3 fwd = transform.TransformDirection(Vector3.forward);
bulletRB.AddForce(fwd*power);
}
}
}
When I press on the Fire1 button the bullet does not shoot. I put (for test) a Debug.Log("BULLET SHOOT") after bulletRB.addForce(). The message is displayed, so the script reached this point. What is wrong with my code?
Based on this somewhat similar question on Unity Answers, you should probably be instantiating the GameObject of the bullet prefab/instance, rather than its Rigidbody directly. Then, access the Rigidbody component of that new bullet and add the force.
Your adjusted Update() method would then look like:
void Update () {
if (Input.GetButtonDown ("Fire1")) {
GameObject newBullet = Instantiate (bullet.gameObject, transform.position, transform.rotation) as GameObject;
RigidBody bulletRB = newBullet.GetComponent<Rigidbody>();
Vector3 fwd = transform.TransformDirection(Vector3.forward);
bulletRB.AddForce(fwd*power);
}
}
Another thing you may want to change is using transform.forward (aka. Forward vector of the turret) rather than Vector3.forward (global forward vector Vector3(0, 0, 1), which may not match the direction of the turret).
Hope this helps! Let me know if you have any questions.
Force can be applied only to an active rigidbody. If a GameObject is inactive, AddForce has no effect.
Wakes up the Rigidbody by default. If the force size is zero then the Rigidbody will not be woken up.
The above description is taken from Unity
Therefore, I would suggest to check if the GameObject is active first.
You can test it by doing the following:
if (newBullet.activeInHierarchy === true)
{
//active
}else{
//inactive
}