How do I keep my player from going over the collider? - c#

I'm making a 2D game where the player moves left and right while dodging falling objects, I put triggers so it doesn't leave the screen but sometimes when it collides with these triggers it passes this collider. I sent an image of the colliders, the Player inspector and also a collider.
Here my Player code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour
{
public bool isRight;
public float speed;
public Transform pointR;
public Transform pointL;
Rigidbody2D rb;
private SpriteRenderer sprite;
private float maxSpeed;
[Header("PowerUps")]
public float force;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
maxSpeed = 600f;
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
isRight = !isRight;
}
}
void FixedUpdate()
{
if(isRight)
{
rb.velocity = new Vector2(speed, 0);
rb.AddForce(rb.velocity,ForceMode2D.Force );
sprite.flipX = true;
if(rb.velocity.x >= maxSpeed)
{
rb.velocity = new Vector2(maxSpeed, 0);
}
}
else
{
rb.velocity = new Vector2(-speed, 0 );
rb.AddForce(rb.velocity, ForceMode2D.Force );
sprite.flipX = false;
}
}
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag =="ColliderL")
{
isRight = !isRight;
}
if(collider.gameObject.tag == "ColliderR")
{
isRight = !isRight;
}
// For PowerUps
if(collider.gameObject.tag == "SpeedUp")
{
StartCoroutine(Seconds());
}
}
IEnumerator Seconds()
{
rb.AddForce(rb.velocity * force);
yield return new WaitForSeconds(5f);
rb.AddForce(rb.velocity * -force);
}
}

I would ideally need to see your scene setup to further pinpoint your mistakes. But these are a few common mistakes that are made:
Tags are case-sensitive. Also, make sure to assign them.
Your collider on one of the object is not 2D; There is Collider, then there is Collider2D.
Your collider is not marked as trigger.
Though there are a few non-related problems with your setup:
rb.velocity.collider doesn't exist. You are looking for rb.velocity.
Ideally you want to set physics changes (velocity, in your case) to FixedUpdate() instead.
If you do want to get colliders from RigidBody, use GetAttachedColliders.
Assuming you are setting up a border which the player can never pass through, you should use a full collider instead of a trigger. This uses OnCollisionEnter2D.
new Vector2(speed, 0* Time.deltaTime);
0 * Time.deltaTime does nothing, since you are multiplying by 0.
What you are looking for is probably new Vector2(speed * Time.deltaTime, 0);. But you don't need to multiply the speed by deltaTime since you are not moving the ball manually via it's Transform.
rb.velocity = new Vector2(speed, 0); should be your result.

Related

Unity3D: How to move an object in relation to another object?

I have a physics enabled sphere in my scene and a character that i can move around using WASD. now what i want is that as soon as player hits the ball it should not be physics enabled anymore and move along with player as like the player is holding it in the hands.
What i have tried so far:
i was able to do it but not at all perfect how i want it to be. i tried OnCollisionEnter to detect the collision, i made the sphere a child of the player, i used isKinematic = true as soon as collision is detected and i used Vector3.MoveTowards for the object to follow the position as the player.
I have attached two reference videos below
Reference Clip(The style i am aiming for):https://www.youtube.com/watch?v=fi-GB0RwLr0
MyVersion(This is what i am able to do):https://www.youtube.com/watch?v=JtV11KHY4pU
overall, if you watch the clips you can see how my version is very rigid, stucky, buggy, not so light weight feeling at all. Cut me a slack as well if i did anything way wrong, i started unity like just in the previous month.
This is my Player script through which i am controlling it all
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Player : MonoBehaviour
{
public float MoveSpeed;
[SerializeField]private Rigidbody playerRig;
[SerializeField] private Rigidbody ballRig;
[SerializeField] private GameObject Ball;
[SerializeField] private GameObject heldObj;
private bool isTouching = false;
private Vector3 offset = new Vector3(0, 1, 3);
private Vector3 force;
public float jump;
private bool isGrounded = true;
private void Start()
{
Ball = GameObject.FindGameObjectWithTag("Ball");
ballRig.GetComponent<Rigidbody>();
playerRig.GetComponent<Rigidbody>();
}
void Update()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
playerRig.velocity = new Vector3(x*MoveSpeed, playerRig.velocity.y,z*MoveSpeed);
Vector3 vel = playerRig.velocity;
vel.y = 0;
if (vel.x != 0 || vel.z != 0)
{
transform.forward = vel;
}
if(transform.position.y < -10)
{
GameOver();
}
ReleaseTheBall();
if (isTouching == true)
{
ballRig.transform.position = Vector3.MoveTowards(ballRig.transform.position, playerRig.transform.position, 5f);
}
}
void Jump()
{
force = new Vector3(0, jump, 0);
if (Input.GetKeyDown(KeyCode.Space) && isGrounded == true)
{
isGrounded = false;
playerRig.AddForce(force, ForceMode.Impulse);
}
}
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "Ball" && heldObj == null)
{
ballRig.isKinematic = true;
// ballRig.constraints = RigidbodyConstraints.FreezeRotation;
ballRig.transform.parent = playerRig.transform;
isTouching = true;
Debug.Log(" BALL PICKED UP AND MOVING ");
heldObj = Ball;
}
}
public void ReleaseTheBall()
{
if (Input.GetKeyDown(KeyCode.Space) && heldObj != null)
{
isTouching = false;
heldObj = null;
ballRig.transform.parent = null;
//ballRig.constraints = RigidbodyConstraints.None;
ballRig.isKinematic = false;
//Vector3 forceDirection = new Vector3(playerRig.transform.position.x, 0, playerRig.transform.position.z);
Vector3 rotationDirection = new Vector3(playerRig.transform.rotation.x, playerRig.transform.rotation.y, playerRig.transform.rotation.z);
ballRig.AddForce(new Vector3(transform.position.x,0,transform.position.z) * 5, ForceMode.Impulse);
ballRig.AddTorque(rotationDirection * 1);
Debug.Log("BALL RELEASED");
}
}
public void GameOver()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
i know there could be some very inefficient lines of code here, feel free to call out those lines as well.
So, basically what i want is that i want my player to pick up the ball as soon as it hits it and the ball should move exactly like player as if he is holding the ball in his hands and when i hit the spacebar i want the ball to be released into the direction i am looking(this part is also very tricky for me), what i am trying to do is i want to do exactly like in the refrence clip. Thanks a lot in advance.
one more thing, when i stop pressing any key to move(WASD), my player transform keeps changing values up and down and it slightly starts moving. i mean when i dont press any key, it still kinda moves.
First of all, you have indeed quite a lot of lines that aren't useful in here and that could be optimized.
Anyway, I hope this will do what you want it to do:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
[RequireComponent(typeof(Rigidbody))]
public class Player : MonoBehaviour {
[Header("Movement Setup")]
[Tooltip("Translation speed in m/s")]
[SerializeField] float _TranslationSpeed;
[Tooltip("Rotation speed in °/s")]
[SerializeField] float _RotationSpeed;
[Tooltip("Interpolation speed [0;1]")]
[SerializeField] float _InterpolationSpeed;
[Tooltip("Strength for jumps")]
[SerializeField] float _JumpingStrength;
[Tooltip("Strength for shoots")]
[SerializeField] float _ShootingStrength;
[Header("Other Settings")]
[Tooltip("Where the helded item will be placed")]
[SerializeField] Transform _ContactPoint;
[Tooltip("Which layers should be considered as ground")]
[SerializeField] LayerMask _GroundLayer;
// The player's rigid body
Rigidbody _Rigidbody;
// Is the player on the ground
bool _IsGrounded;
// Rigidbody of what's the player is helding. Null if the player isn't holding anything
Rigidbody _HeldedItemRigidbody;
// Getter to _IsGrounded, return true if the player is grounded
public bool IsGrounded { get { return _IsGrounded; } }
private void Start() {
_Rigidbody = GetComponent<Rigidbody>(); // The player's rigidbody could be serialized
}
void FixedUpdate() {
float horizontalInput, verticalInput;
// Getting axis
horizontalInput = Input.GetAxis("Horizontal");
verticalInput = Input.GetAxis("Vertical");
// Moving toward the x
Vector3 moveVect = transform.forward * _TranslationSpeed * Time.fixedDeltaTime * verticalInput;
_Rigidbody.MovePosition(_Rigidbody.position + moveVect);
// Rotating the player based on the horizontal input
float rotAngle = horizontalInput * _RotationSpeed * Time.fixedDeltaTime;
Quaternion qRot = Quaternion.AngleAxis(rotAngle, transform.up);
Quaternion qRotUpRight = Quaternion.FromToRotation(transform.up, Vector3.up);
Quaternion qOrientationUpRightTarget = qRotUpRight * _Rigidbody.rotation;
Quaternion qNewUpRightOrientation = Quaternion.Slerp(_Rigidbody.rotation, qOrientationUpRightTarget, Time.fixedDeltaTime * _InterpolationSpeed); // We're using a slerp for a smooth movement
_Rigidbody.MoveRotation(qRot * qNewUpRightOrientation);
// This prevents the player from falling/keep moving if there is no input
if (_IsGrounded) _Rigidbody.velocity = Vector3.zero;
_Rigidbody.angularVelocity = Vector3.zero;
if (transform.position.y < -10) GameOver();
if (Input.GetKey(KeyCode.Mouse0) && _HeldedItemRigidbody != null) ShootTheBall();
if (!_IsGrounded) return; // What's below this won't be executed while jumping
if (Input.GetKey(KeyCode.Space)) Jump();
}
void Jump() {
_IsGrounded = false;
_Rigidbody.AddForce(_JumpingStrength * transform.up, ForceMode.Impulse);
}
private void OnCollisionEnter(Collision collision) {
// Checking if the player encountered a ground object
if ((_GroundLayer & (1 << collision.gameObject.layer)) > 0) _IsGrounded = true;
if (collision.gameObject.tag == "Ball") { // this can be improved as tags are error prone
// Setting the item position to contact point
collision.gameObject.transform.position = _ContactPoint.position;
// Getting the rigidbody
_HeldedItemRigidbody = collision.gameObject.GetComponent<Rigidbody>();
// Setting the parent
_HeldedItemRigidbody.transform.parent = transform;
// Setting the body to be kinematic
_HeldedItemRigidbody.isKinematic = true;
// Stopping any movement or rotation
_HeldedItemRigidbody.velocity = Vector3.zero;
_HeldedItemRigidbody.angularVelocity = Vector3.zero;
}
}
public void ShootTheBall() {
// Reverting what's done in OnCollisionEnter
_HeldedItemRigidbody.transform.parent = null;
_HeldedItemRigidbody.isKinematic = false;
// Adding force for the shoot
_HeldedItemRigidbody.AddForce(transform.forward * _ShootingStrength, ForceMode.Impulse);
// Resetting helding item to null
_HeldedItemRigidbody = null;
}
public void GameOver() {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
You have to add a layer, and set the ground to this new layer.
Set up the variable as you like, don't hesitate to mess up with it a little bit to find what suits you (and don't forget to set up the player's mass (in rigidbody) as well!!)
Exemple of setup for the player script
Last things, you have to add an empty game object to your player which will be where the ball will be placed. I know it's a little bit dirty but place it a bit far from the player (more than the ball's radius) or it will bug. We could do something programatically to fix the ball at a spot and adapt this spot to the ball's radius.
This is an example of player
As you can see, I have the empty "player" gameobject which has the rigidbody, the collider and the player script.
The GFX item which is just the capsule.
If you don't know about this: the player game object is actually on the floor (at the player's feet) so it's better for movements. If you do this, don't forget to adjust the player's capsule collider to fit the GFX.
I also have the contact point.
Forget about the nose stuff, it's just a way to see where the player is facing when you just have a capsule.
If you have any question, feel free to ask.

Rigidbody.Usegravity on collision enter doesnt work

Im throwing a bomb using physics made by code. For some reason it doesnt detect collision unless my physics stop applying force to the object. in order to bypass it I cancelled the applied force and applied gravity to it on collision enter and put the movement of the bomb to LateUpdate so it will trigger after the OnCollisionEnter but the bomb collides only most of the time with the floor (mesh collision, the floor made with ProBuilder) and not all of the time. the bomb collision detection is set to continuous
Will appreciate all the help, Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BombBehavior : MonoBehaviour
{
[SerializeField] float ExplosionForce = 300;
[SerializeField] float ExplosionRadius;
[SerializeField] float LaunchForceX;
[SerializeField] float LaunchForceY;
[SerializeField] float Delay;
float countdown;
Rigidbody rigidbodyy;
float gravity = 1;
Player player;
bool HasExploded = false;
// Start is called before the first frame update
void Start()
{
rigidbodyy = GetComponent<Rigidbody>();
player = Player.p;
GetLaunchForceX();
countdown = Delay;
}
private void GetLaunchForceX()
{
if (transform.position.x > player.transform.position.x)
{
LaunchForceX *= 1;
}
else if (transform.position.x < player.transform.position.x)
{
LaunchForceX *= -1;
}
}
private void LateUpdate()
{
ThrowBomb();
}
private void Update()
{
countdown -= Time.deltaTime;
if (countdown <= 0 && !HasExploded)
{
ExplodeNearEnemy();
}
}
private void ThrowBomb()
{
if (rigidbodyy.useGravity == false)
{
Vector3 ThrowDirection = new Vector3(LaunchForceX, LaunchForceY, 0);
LaunchForceY -= gravity;
ThrowDirection.y = LaunchForceY;
transform.Translate(ThrowDirection * Time.deltaTime);
}
}
private void ExplodeNearEnemy()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, ExplosionRadius);
foreach (Collider collider in colliders)
{
if (collider.gameObject.CompareTag("Enemy"))
{
Rigidbody enemyRB = collider.GetComponent<Rigidbody>();
if (enemyRB != null)
{
enemyRB.useGravity = true;
enemyRB.AddExplosionForce(ExplosionForce, transform.position, ExplosionRadius);
Destroy(enemyRB.gameObject,1);
}
}
}
//Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
LaunchForceY = 0;
LaunchForceX = 0;
gravity = 0;
rigidbodyy.useGravity = true;
}
}
I don't exactly understand the problem but it looks like you want to make a Bomb/Granate so why dont you just write a function and use AddForce()?
void Thow(Vector3 direction, float strength)
{
rigidbodyy.AddForce(direction * strength, ForceMode.Impulse);
}
something like this should help as you only need to get the throwing direction and apply a strength then Unity will handle the rest
Or maybe if your bomb doesn't collide with the ground give the ground a rigidbody and set kinematic to true
Use the MovePosition() to move a rigid body if you want colliders etc to work. Teleporting a rigid body by altering the transform directly messes up the physics.

Enemy not moving

In my game I want to have one of my enemies move towards the player however they don't do that for some reason. The enemy is supposed to look at the player and when the player enters the range they should start moving towards him. My problem is that they aren't doing anything. They just stand there. Also they don't even fall when they have a rigidbody. It currently has an Animator, box collider, and a capsule collider.
Edit: I forgot to add this but the script also triggers the animations
Edit #2: Also I know that it isn't because the movement is in the if statement
(Sorry if it is bad I am a programmer noob)
This is the script responsible for the players movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mar_Tracker : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 3.0f;
public float InRadius = 250.0f;
public float AttackRange = 11.0f;
public float rocketRange = 50.0f;
private Animator anim;
private Coroutine RocketLouch = null;
public GameObject Rocket;
public GameObject Explosion;
public SphereCollider sphereCollider;
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
sphereCollider.enabled = false;
}
void Update()
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player); // Makes it so that the enemy looks at player
float dstSqr = (Player.position - transform.position).sqrMagnitude;
bool inRadius = (dstSqr <= InRadius * InRadius);
bool inAttackRange = (dstSqr <= AttackRange * AttackRange);
bool inRocketRange = (dstSqr <= rocketRange * rocketRange);
anim.SetBool("inArea", inRadius);
anim.SetBool("Attacking", inAttackRange);
if (inRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime; // (movement)
}
if (inRocketRange)
{
if (RocketLouch == null)
{
RocketLouch = StartCoroutine(RocketLaunch());
}
}
}
IEnumerator RocketLaunch()
{
anim.SetBool("Rocket", true);
yield return new WaitForSeconds(0.15f);
sphereCollider.enabled = true;
Explosion.SetActive(true);
yield return new WaitForSeconds(1.0f);
anim.SetBool("Rocket", false);
Destroy(Rocket);
}
}
Your using math that may be a tad to complicated for what you want to achieve and I think the issue is happening there.
Lets simplify it
Transform Player;
float InRadius;
float AttackRange;
float rocketRange;
void Start()
{
// Set it at the start to optimize performance
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Use fixed Update for moving things around
// Better performance
void FixedUpdate()
{
HandlePlayerDetection() ;
}
void HandlePlayerDetection()
{
transform.LookAt(Player); // Makes it so that the enemy looks at player
// Find the distance between the player and the transform
var distance = Vector3.Distance(transform.position, player.position);
// Do the boolean calculations
bool inRadius = distance <= InRadius;
bool inAttackRange = distance <= AttackRange;
bool inRocketRange = distance <= rocketRange;
// Lets use inbuilt functions to movement
if (inRadius)
{
transform.position = Vector3.MoveTowards(transform.position, Player.position, MoveSpeed * Time.deltaTime);
}
// Rocket code
if (inRocketRange)
{
FireRocket();
}
}
void FireRocket()
{
if (RocketLouch == null)
{
RocketLouch = StartCoroutine(RocketLaunch());
}
}
Now if you really want your enemy to walk properly I would recommend watching a tutorial on Nav Meshes. It is super easy to use and will allow the enemy to walk around objects and do propper path finding.

Player Momentarily Stuck at Edges of Screen! Unity

i am working on a Unity 2d game thats rounded about a player dodging random objects while flying...something like flappybird. the game work normal.
But the problem is when the player when touch the top screen edge and keep adding force the player get stuck a while on the edge. i think i need to disable adding force when the player touch the edge of the screen.
I hope u guys help me.
This the player code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jetpack: MonoBehaviour
{
public GameManager GameManager;
private Rigidbody2D rb;
private float jumpForce = 40f;
private bool engineIsOn;
[SerializeField] private GameObject fire;
void Start()
{
engineIsOn = false;
fire.SetActive(false);
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.Space))
{
engineIsOn = true;
fire.SetActive(true);
}
else
{
engineIsOn = false;
fire.SetActive(false);
}
transform.position = new Vector3(Mathf.Clamp(transform.position.x, -4.5f, 4.5f),
Mathf.Clamp(transform.position.y, -4.5f, 4.5f), transform.position.z);
}
private void FixedUpdate()
{
switch (engineIsOn)
{
case true:
rb.AddForce(new Vector2(0f, jumpForce),ForceMode2D.Force);
break;
case false:
rb.AddForce(new Vector2(0f, 0f), ForceMode2D.Force);
break;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
GameManager.gameover();
}
}
Whenever dealing with Rigidbody then don't set any values via the Transform component!
This might break the physics.
And then basically you already said it: Don't add more upwards force if you are on the edges.
As a simple trick you could set the Y velocity to 0 when hitting the edges like
private void FixedUpdate()
{
// Very unusual to use a switch for a single bool
if (engineIsOn)
{
rb.AddForce(new Vector2(0f, jumpForce),ForceMode2D.Force);
}
// This does absolutely nothing
// Adding a force of 0,0 doesn't change the velocity at all ...
//else
//{
// rb.AddForce(new Vector2(0f, 0f), ForceMode2D.Force);
//}
// Check if currently hitting an edge
// and if so set the velocity on that axis to 0
// also clamp the position then
var velocity = rb.velocity;
var position = rb.position;
if(Mathf.Abs(rb.position.x) >= 4.5f)
{
velocity.x = 0;
position.x = Mathf.Clamp(position.x, -4.5f, 4.5f);
}
if(Mathf.Abs(rb.position.y) >= 4.5f)
{
velocity.y = 0;
position.y = Mathf.Clamp(position.y, -4.5f, 4.5f);
}
rb.velocity = velocity;
rb.position = position;
}

How to make a sprite slower when it moves over a sprite?

I have a game object called "Player" and my map is made up of short grasses and long grasses. If my player is on the long grass, I want it to slow down. One additional problem is that there are multiple Long grasses game objects in my game. Here is a screenshot of them :
https://imgur.com/hUq4baV
This is what my current movement code looks like :
public class PlayerMovement : MonoBehaviour
{
public Sprite Up;
public Sprite Down;
public Sprite Right;
public Sprite Left;
public float speed;
private SpriteRenderer sr;
// Update is called once per frame
void Update()
{
Vector3 move;
if (Input.GetKey(KeyCode.W))
{
GetComponent<SpriteRenderer>().sprite = Up;
move = new Vector2(0, speed * Time.deltaTime);
transform.position += move;
}
if (Input.GetKey(KeyCode.A))
{
GetComponent<SpriteRenderer>().sprite = Left;
move = new Vector2(speed * Time.deltaTime, 0);
transform.position -= move;
}
if (Input.GetKey(KeyCode.D))
{
GetComponent<SpriteRenderer>().sprite = Right;
move = new Vector2(speed * Time.deltaTime, 0);
transform.position += move;
}
if (Input.GetKey(KeyCode.S))
{
GetComponent<SpriteRenderer>().sprite = Down;
move = new Vector2(0,speed * Time.deltaTime);
transform.position -= move;
}
}
}
// All my code does is that on each WASD, it changes to a different sprite and moves it.
An explanation would be appreciated because I'm a beginner to this.
use colliders, eg. boxcolliders to detect if player is touching the grass.
Use a tag on you player
use the oncollisionenter2d() method on the grass
void OnCollisionEnter2D(Collision2D col) {
if(col.tag == "PlayerTag") {
playerScriptWhereSpeedIsLocated.speed = 1 //the speed you want
} }
One solution to this would be to use 2D Trigger Colliders.
Assuming your player has some sort of 2D collider attached to it, you may attach trigger colliders to your grass objects which can then send a message to the PlayerMovement script when the player enters/leaves the trigger by adding the OnTriggerEnter2D and OnTriggerExit2D methods to your PlayerMovement class. Here's an example of how this might work:
void OnTriggerEnter2D(Collider2D col)
{
// You can use gameObject.tag to determine what type of object we're colliding with
if(col.gameObject.tag == "LongGrass"){
speed = .8f;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.tag == "LongGrass"){
speed = 1f;
}
}
I would encourage you to look into the Collision action matrix if you are having
trouble detecting collisions in this way. You'll also make sure you tag your grass correctly for this method to work.

Categories

Resources