I'm currently trying to make a Unity3d (Or 2D in this case) Platformer tech demo.
While working on the demo I ran into a bit of a snag because I want my Player object to be able to go up slopes, but if I apply gravity while the Player object is on the ground it will not be able to go up slopes, even if it can go down them. My solution was to turn off gravity while the PLayer object was touching the ground, and turn it back on when it wasn't. I did this by using the functions void OnCollisionEnter2D(Collision2D collision) to turn the gravity off, and void OnCollisionExit2D(Collision2D collision) to turn it back on.
This did not work, so I played with it a bit and thought of the idea to give the ground, and Player object "Trigger Boxes" which are unseen child Cube objects that have a Collider marked as a Trigger. So now I am using the function void OnTriggerEnter2D(Collider2D collision) to turn the gravity off, and void OnTriggerExit2D(Collider2D collision) to turn it back on. But this also does not work. Below is my code, first is the Player object's main script, and after that is the Player object's "TriggerBox" and after that will be images to show how my objects are setup in Unity.
The result: The Player class currently does not collide with the ground, and falls through it to infinity.
What I want: The player to collide with the ground, and the player's TriggerBox to collide with the ground's "TriggerBox" and turn off the Player's gravity.
Notes: I've tried giving the players, and ground RigidBody2Ds. The player collides with the ground and gets jittery, and does not trigger the gravity turn-off, and the ground falls on contact with the player when the ground has the RigidBody2D. It is unknown what triggers, as the ground falls right under the player.
using UnityEngine;
using System.Collections;
public class PlayerControls : MonoBehaviour {
CharacterController controller;
private float speed = 0.004f;
private float fallSpeed = 0.01f;
private Vector3 tempPos;
bool onGround = false;
public void setOnGround(bool b){ onGround = b; }
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
tempPos = Vector3.zero;
if(Input.GetKey(KeyCode.W)) tempPos.y -= speed;
if(Input.GetKey(KeyCode.S)) tempPos.y += speed;
if(Input.GetKey(KeyCode.A)) tempPos.x -= speed;
if(Input.GetKey(KeyCode.D)) tempPos.x += speed;
RaycastHit2D[] hits = Physics2D.RaycastAll(new Vector2(transform.position.x,transform.position.y), new Vector2(0, -1), 0.2f);
float fallDist = -fallSpeed;
if(hits.Length > 1){
Vector3 temp3Norm = new Vector3(hits[1].normal.x, hits[1].normal.y, 0);
Vector3 temp3Pos = new Vector3(tempPos.x, tempPos.y, 0);
temp3Pos = Quaternion.FromToRotation(transform.up, temp3Norm) * temp3Pos;
tempPos = new Vector2(temp3Pos.x, temp3Pos.y);
}
if(!onGround) tempPos.y = fallDist;
transform.Translate(tempPos);
}
}
Next the TriggerBox:
using UnityEngine;
using System.Collections;
public class PlayerTriggerBox : MonoBehaviour {
public PlayerControls playerControls;
// Use this for initialization
void Start () {
//playerControls = gameOGetComponent<PlayerControls>();
if(playerControls == null) Debug.Log("playerControls IS NULL IN PlayerTriggerBox!");
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter2D(Collider2D other) {
playerControls.setOnGround(true);
}
void OnTriggerExit2D(Collision2D collision){
playerControls.setOnGround(false);
}
}
-As for the images for my setup-
The player's setup:
The player's TriggerBox setup:
The ground's setup:
The ground's TriggerBox setup:
Thank you for your time reading this, and hopefully for your help.
Related
(New to Stackoverflow btw) So I've currently working on a 2D top-down Zelda like game and I've made a reusable knockback script that can be assigned to enemies. I have received no errors in the console, but when the Player collides with Enemy nothing happens Player still receives damage but there is no knockback or response from the Debug.Log
Following Code for Knockback:
public class Knockback_Script : MonoBehaviour
{
//balls
public float Power;
void Start()
{
}
void OnCollisionEnter2D(Collider2D other)
{
//Make a custom tag OR LAYER where the player gets affected by all
//or just make another "OnTiggerEnter2D"
if (other.gameObject.CompareTag("Player"))
{
Rigidbody2D Player = other.GetComponent<Rigidbody2D>();
Vector2 difference = Player.transform.position - transform.position;
difference = difference * Power;
Player.AddForce(difference, ForceMode2D.Impulse);
Debug.Log("WORKS");
}
}
}
Change Collider2D to Collision2D.
OnCollisionEnter2D() need Collision2D.
OnTriggerEnter2D() need Collider2D.
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.
As you can see in the screenshoot I can't see prefabs in the game tab but only in the editor. I have made a simple function for shooting(not finished yet), it works fine, it spawns the prefabs but i can't see them in the game tab, I have already tried changing the Sorting Layer, move the camera, change Z position but nothing appen.
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAttack : MonoBehaviour
{
[SerializeField]
float delayBetweenShots = 0.4f;
float timePassedSinceLast = 0f;
// Start is called before the first frame update
void Start()
{
timePassedSinceLast = delayBetweenShots;
}
// Update is called once per frame
void Update()
{
Aiming();
Shooting();
}
void Aiming()
{
var objectPos = Camera.main.WorldToScreenPoint(transform.position);
var dir = Input.mousePosition - objectPos;
transform.rotation = Quaternion.Euler(new Vector3(0,0,Mathf.Atan2(-dir.x, dir.y) * Mathf.Rad2Deg));
}
void Shooting()
{
if(Input.GetMouseButton(0) && timePassedSinceLast >= delayBetweenShots)
{
GameObject bullet = (GameObject)Instantiate(Resources.Load("bullet"), transform.position, transform.rotation);
timePassedSinceLast = 0f;
}
else
{
timePassedSinceLast += Time.deltaTime;
}
}
}
The prefabs get instantiated correctly. As others suggested as well, the best way to find "lost" objects in your game is to shoot some stuff, pause the game, go into scene view, turn on 3D mode and double click one of the prefabs in the hierarchy. The camera will take you straight to your object.
I'm writing a script in C# in Unity that essentially functions as a switch to turn gravity on or off for a 2D Rigidbody. When the game starts, gravity should be 0 for the Rigidbody. Then when the user taps the space bar, gravity is supposed to increase to 3 (which is does). Then, however, when the player collides with a gameObject labeled 'InvisGoal', the player should teleport to another location and then gravity should be 0 again. However, the player always falls after coming in contact with InvisGoal and teleporting and I can't figure out why. This is my first project in C# so sorry for any obvious errors.. The script is here:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallLaunch : MonoBehaviour {
private Rigidbody2D myRigidBody;
public GameObject Ball;
// Use this for initialization
void Start ()
{
myRigidBody = GetComponent<Rigidbody2D> ();
GetComponent<Rigidbody2D>().gravityScale = 0f;
}
// Update is called once per frame
void Update ()
{
if (Input.GetButtonDown ("Jump"))
{
GetComponent<Rigidbody2D> ().gravityScale = 3f;
}
}
void OnTriggerEnter2D(Collider2D other){
if (other.tag == "InvisGoal")
{
Ball.gameObject.GetComponent<Rigidbody2D>().gravityScale = 0f;
transform.position = new Vector3 (0.61f, 1.18f, 0f);
return;
}
}
}
Ball.gameObject.GetComponent<Rigidbody2D>().gravityScale = 0f;
This is likely what is causing the problem.
It sounds like the RigidBody2D you are referencing to in this line is not the same as the one you retrieved beforehand with GetComponent().
GetComponent returns the component of the GameObject you call it from. Therefore in the code I mentioned above,
Ball.gameObject.GetComponent<RigidBody2D>()
and
GetComponent<RigidBody2D>()
would give you an two different RigidBody2D component if the field Ball does not refer to the same GameObject your BallLaunch script is attached to.
[
Supposing BallLaunch script is attached to the Ball you want to set the gravity of (As picture above)
Simply change:
Ball.gameObject.GetComponent<Rigidbody2D>().gravityScale = 0f;
To
GetComponent<Rigidbody2D>().gravityScale = 0f;
Also, since you already referenced your RigidBody2D in your Start method to the field myRigidBody, you can replace all subsequent GetComponent with myRigidBody.
GetComponent<Rigidbody2D>().gravityScale = 0f;
To
myRigidBody.gravityScale = 0f;
I am creating a game in Unity where I have to have a player (a ball) and three enemies (in this case three rotating cylinders). Whenever the player hits an enemy, I need it to die (which I have already done) and then print out Game over, which I don't know how to do. I also need the player to respawn after it dies, which is another think I don't know how to do. I also need to create three "virtual holes" where when the player rolls over them, it respawns, but not dies. I figure I can simulate holes by creating flat cylinders, but I don't know how to make the ball respawn but rolling over them. Thank you in advance!! Please be clear about which part does what in your answer.
//My player script
public class PlayerController : MonoBehaviour
{
public float speed;
private Rigidbody rb;
public float threshold;
public Text gameOver;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
//rolls the player according to x and z values
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
rb.AddForce(movement * speed);
}
}
//my script that makes the enemy rotate and kills the player but after the
player dies it just disappears
public class Rotater : MonoBehaviour {
public Text gameOver;
// Update is called once per frame
void Update ()
{
transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}
//kills player
void OnCollisionEnter(Collision Col)
{
if (Col.gameObject.name == "Player")
{
Destroy(Col.gameObject);
gameOver.text = "Game over!";
}
}
//my script that respawns the play if it falls off the maze
public class Respawn : MonoBehaviour
{
// respawns player if it goes below a certain point (falls of edge)
public float threshold;
void FixedUpdate()
{
if (transform.position.y < threshold)
transform.position = new Vector3(-20, 2, -24);
}
}
You need to create a UI to draw text over your game. You can learn about it here.
When you have the UI in place, you can just activate/deactivate the relevant parts with SetActive(bool).
To kill and respawn the player, I suggest you not to destroy it and re-instantiate it. Instead, you can simply deactivate it and reactivate it in the new position using SetActive(bool) again.
For the holes, you can create other objects with colliders and use OnCollisionEnter as you already did but to change player's position.