I'm currently trying to use a Physics2D.BoxCast() to manually detect collisions in a test project. I got everything set up with the help of a tutorial I watched a while ago but for some reason the box cast seems to be above the Box Collider that I attached its size to. I'm not the best a writing issues out so Ill have a gif bellow that better shows what the problem is, as well as I'll attach the code I used to detect the collisions.
This is when I try to collide from +y on the axis
This is when I try to collide from -y on the axis:
I've tried looking up more on how Physics2D.BoxCast work and why it would seem to be pushed down but I havent found anything helpful just yet.
Here's the code I use for movement and manual collision detection.
using UnityEngine;
public class Player : MonoBehaviour {
public float speed = 2f;
private BoxCollider2D boxCollider;
private SpriteRenderer sprite;
private Vector3 moveDelta;
private RaycastHit2D hit2D;
protected virtual void Awake() {
boxCollider = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
}
protected virtual void Update() {
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
moveDelta = new Vector3(x, y, 0);
// Flips the sprite based on the direction its moving on the X
if (moveDelta.x > 0)
{
sprite.flipX = false;
}
if (moveDelta.x < 0)
{
sprite.flipX = true;
}
// Manual detection for collisions
hit2D = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.x * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);
}
hit2D = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
}
}
}
The transform.position used in the box cast was not at the center of the collider. Thus casting the BoxCast above where the collider was. The solution was to place an empty game object in the center of the collider. Again I'm sorry about the way I explain things I hope the pictures bellow can help you understand better..
Code wise it was a simple fix that took me too long to figure out..
using UnityEngine;
public class Player : MonoBehaviour {
public float speed = 2f;
public GameObject boxcastCenter;
private BoxCollider2D boxCollider;
private SpriteRenderer sprite;
private Vector3 moveDelta;
private RaycastHit2D hit2D;
protected virtual void Awake() {
boxCollider = GetComponent<BoxCollider2D>();
sprite = GetComponent<SpriteRenderer>();
}
protected virtual void Update() {
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
moveDelta = new Vector3(x, y, 0);
// Flips the sprite based on the direction its moving on the X
if (moveDelta.x > 0)
{
sprite.flipX = false;
}
if (moveDelta.x < 0)
{
sprite.flipX = true;
}
// Manual detection for collisions changes were made in the transform.position bellow
hit2D = Physics2D.BoxCast(boxcastCenter.transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.x * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(moveDelta.x * Time.deltaTime, 0, 0);
}
hit2D = Physics2D.BoxCast(boxcastCenter.transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("Blocking", "Actor"));
if (hit2D.collider == null)
{
transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
}
}
}
Related
i'm trying to make a game on unity using C# for a games dev course. the course is: https://www.youtube.com/watch?v=b8YUfee_pzc and at 51:55 is where i am experiencing the error. the error is NullReferenceException: Object reference not set to an instance of an object player.FixedUpdate () (at Assets/scripts/player.cs:32)
i have checked and it seems to be letter by letter (including caps) perfect, unless i'm jsut stupid.
it may be because of outdated code, something in unity or me just copying something down wrong. anyhow, here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class player : MonoBehaviour
{
private BoxCollider2D boxCollider;
private RaycastHit2D hit;
private Vector3 moveDelta;
private void start(){
boxCollider = GetComponent<BoxCollider2D>();
}
private void FixedUpdate()
{
float x = Input.GetAxisRaw("Horizontal");
float y = Input.GetAxisRaw("Vertical");
// reset move delta
moveDelta = new Vector3(x, y, 0);
// swap sprite direction
if(moveDelta.x > 0)
transform.localScale = Vector3.one;
else if (moveDelta.x < 0)
transform.localScale = new Vector3(-1,1,1);
hit = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(0, moveDelta.y), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("actor", "blocking"));
if (hit.collider == null)
{
transform.Translate(0, moveDelta.y * Time.deltaTime, 0);
}
hit = Physics2D.BoxCast(transform.position, boxCollider.size, 0, new Vector2(moveDelta.x, 0), Mathf.Abs(moveDelta.y * Time.deltaTime), LayerMask.GetMask("actor", "blocking"));
if (hit.collider == null)
{
transform.Translate( moveDelta.x * Time.deltaTime, 0, 0);
}
}
}```
Found a comment on that video that worked for me: "changing "private BoxCollider2D boxCollider;" to "public BoxCollider2D boxCollider;" then dragging the box collider in to the tab on the script under your npc"
Im new on programming and I tried to transform the code from 2d into 3d (from Physics.Overlapcircle into Physics.Overlapsphere), but it still doesn't work.
I also looked for the problem on Stack Overflow, but with this tip it also doesn't work...
I make the Controller for a grid based game... All works, but I can move through obstacles. Is it wrong to make it with Overlap Sphere? Do you have another idea? Thanx
Here is the code:
public class MovementPlayer1 : MonoBehaviour
{
public float moveSpeed = 5f;
public Transform movePoint;
public Transform forward, back, left, right;
public Collider[] colliders;
public LayerMask whatStopsMovement;
void Start()
{
movePoint.parent = null;
}
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, movePoint.position, moveSpeed * Time.deltaTime); //Time.deltaTime, damit es überall gleich schnell ist
if (Vector3.Distance(transform.position, movePoint.position) <= .05f)
{
if (Mathf.Abs(Input.GetAxisRaw("Horizontal")) == 1f) //Mathf.Abs fragt, ob es entweder positiv oder negativ ist, bzw. falls Input.GetAxis... sich irgendwie bewegt
{
colliders = Physics.OverlapSphere(movePoint.position + new Vector3(Input.GetAxisRaw("Horizontal"), 0, 0), 0.2f, whatStopsMovement);
if (colliders != null)
{
movePoint.position += new Vector3(Input.GetAxisRaw("Horizontal"), 0, 0);
}
}
if (Mathf. Abs(Input.GetAxisRaw("Vertical")) == 1f)
{
colliders = Physics.OverlapSphere(movePoint.position + new Vector3(0, 0, Input.GetAxisRaw("Vertical")), 0.2f, whatStopsMovement);
if (colliders != null)
{
movePoint.position += new Vector3(0, 0, Input.GetAxisRaw("Vertical"));
}
}
}
}
}
I did it with the SweepTest and it worked. I don't know why it doesn't work for me with Physics.OverlapSphere. For guys who found this question, I would recommend you to do it with the SweepTest.
Thanks to all for your help!
The issue is here:
if (colliders != null)
"colliders" is an array, unity doesn't like that comparison array vs null. Instead try checking the arrays size:
if (colliders.Length > 0)
For handling movement input if you insist on using transform.position, then instead of having 2 if statements I would suggest you change it to something like that:
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, movePoint.position, moveSpeed * Time.deltaTime); //Time.deltaTime, damit es überall gleich schnell ist
float movementX = Input.GetAxisRaw("Horizontal");
float movementZ = Input.GetAxisRaw("Vertical");
Vector3 newPosition = new Vector3(movementX, 0, movementZ);
if (Vector3.Distance(transform.position, movePoint.position) <= .05f)
{
colliders = Physics.OverlapSphere(movePoint.position + newPosition , 0.2f, whatStopsMovement);
if (colliders.Length > 0)
{
movePoint.position += newPosition;
}
}
}
Aerial Movement Problem
I've followed a few tutorials and I've set up a decent base for a game, - basic player movement, entity animation, health, HUD UI, etc. - but I wanted to make the movement a bit smoother, less snappy, more physics-based, you get the gist. I made it a bit better and it definitely meets all the requirements for terrain-based movement (i.e: movement across physical surfaces - while the player is on the ground), but I want jumping to be a bit more dynamic, and I know what I want to do but I'm having trouble doing it.
Here's the issue:
If the player moves (regardless of whether sprintActive is toggled or not) and they jump after moving, the player gains speed in the air way too fast, and they can't move in any direction other than forward (relative to their rotation).
Here's the objective:
I set it up so the velocity is simply redirected to whatever direction is forward from the playerRB, but I think I would prefer if the velocity didn't immediately change direction, but rather gradually curve as the player tries to move a certain direction? I know I may be asking for a lot, but some advice on how to do this, or if I should try a different approach, would be very much appreciated.
Code used:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RFPSPlayerMovement : MonoBehaviour
{
public float speedForce;
public float jumpForce;
public float diveForce;
public Transform groundCheck;
public float groundDistance = 0.25f;
public LayerMask groundMask;
private Transform playerBody;
private Rigidbody playerRB;
private bool isGrounded;
private bool sprintActive = false;
private bool delay = false;
private float lateralSpeed;
private Vector3 velocity;
float xMov, zMov;
float xSpeed, zSpeed;
IEnumerator DelayTimer(float duration)
{
delay = true;
yield return new WaitForSeconds(duration);
delay = false;
}
void Start()
{
playerBody = GetComponent<Transform>();
playerRB = GetComponent<Rigidbody>();
}
void Update()
{
xMov = Input.GetAxisRaw("Horizontal") * Time.deltaTime;
zMov = Input.GetAxisRaw("Vertical") * Time.deltaTime;
if(Input.GetKey(KeyCode.LeftShift))
{
sprintActive = true;
}
else
{
sprintActive = false;
}
if(Input.GetKey(KeyCode.W))
{
Movement(0, speedForce);
}
if(Input.GetKey(KeyCode.S))
{
Movement(0, -speedForce);
}
if(Input.GetKey(KeyCode.A))
{
Movement(-speedForce, 0);
}
if(Input.GetKey(KeyCode.D))
{
Movement(speedForce, 0);
}
if(Input.GetKey(KeyCode.Space))
{
if(delay == false)
{
if(isGrounded)
{
StartCoroutine(DelayTimer(0.05f));
Jump(jumpForce);
}
}
}
}
void FixedUpdate()
{
velocity = playerRB.velocity;
lateralSpeed = new Vector2(playerRB.velocity.x, playerRB.velocity.z).magnitude;
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if(isGrounded)
{
velocity = new Vector3(
xMov + (float) (playerRB.velocity.x * 0.90),
playerRB.velocity.y,
zMov + (float) (playerRB.velocity.z * 0.90)
);
}
else
{
velocity.x = (float) (playerBody.forward.x * lateralSpeed);
velocity.y = playerRB.velocity.y;
velocity.z = (float) (playerBody.forward.z * lateralSpeed);
}
playerBody.eulerAngles = new Vector3(0, playerBody.eulerAngles.y, 0);
playerRB.velocity = velocity;
}
void Movement(float xAmount, float zAmount)
{
if(sprintActive)
{
xSpeed = (float) (xAmount * 2);
zSpeed = (float) (zAmount * 2);
}
else
{
xSpeed = xAmount;
zSpeed = zAmount;
}
playerRB.AddRelativeForce(new Vector3(xSpeed, 0, zSpeed), ForceMode.Impulse);
}
void Jump(float yAmount)
{
playerRB.AddRelativeForce(new Vector3(0, yAmount, 0), ForceMode.Impulse);
}
}
I'm trying to make the enemy object face the end of the line cast. The code I have locks the ray onto the player when detected properly. My problem here is that my enemy sprite is a grandchild of the Rigidbody2D and I'm using eulerAngles to change the sprites direction. Here is the code I have:
using UnityEngine;
public class PlayerDetection : MonoBehaviour
{
public Transform origin, end, player;
public float radarSpd;
public bool playerDetected;
public static bool playerIsDetected;
private int playerLayer = 1 << 8;
private Rigidbody2D enemyRb;
private Vector3 facePlayer;
private void Start()
{
enemyRb = GetComponentInParent<Rigidbody2D>();
playerIsDetected = false;
}
private void Update()
{
PlayerDetector();
if (playerDetected == false)
{
Radar();
}
else { PlayerIsDetected(); }
}
void PlayerDetector()
{
Debug.DrawLine(origin.position, end.position, Color.red);
playerDetected = Physics2D.Linecast(origin.position, end.position, playerLayer);
}
void Radar()
{
end.RotateAround(origin.position, Vector3.forward, radarSpd * Time.deltaTime);
}
void PlayersPosition()
{
facePlayer = player.position - enemyRb.transform.GetChild(0).GetChild(0).position;
float enemyRot = Mathf.Atan2(facePlayer.y, facePlayer.x) * Mathf.Rad2Deg;
enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, enemyRot);
}
void PlayerIsDetected()
{
if(playerDetected == true)
{
playerIsDetected = true;
end.position = player.position;
PlayersPosition();
}
}
}
The main focus for this code that I need help is here:
void PlayersPosition()
{
facePlayer = player.position - enemyRb.transform.GetChild(0).GetChild(0).position;
float enemyRot = Mathf.Atan2(facePlayer.y, facePlayer.x) * Mathf.Rad2Deg;
enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, enemyRot);
}
The enemy does not face the player at all and doesn't seem to rotate much at all as the player moves around the screen.
I've tried applying a rotationSpeed variable and multiplied it by enemyRot multiplied by Time.deltaTime but didn't work either.
enemyRb.transform.GetChild(0).GetChild(0).eulerAngles = new Vector3(0, 0, enemyRot * rotSpd * Time.deltaTime);
I've been stuck on this for a week now and really need some help resolving this issue! So how can I achieve my desired rotation by use of eulerAngles?
Turned out that I was doing too much math. Here is the solution:
Edit: My enemy is facing in an upward position. This code works on that scale.
void PlayersPosition()
{
facePlayer = player.position - enemyRb.transform.GetChild(0).GetChild(0).position;
enemyRb.transform.GetChild(0).GetChild(0).up = -facePlayer;
}
I've been trying to rotate my player's gun target along with the player. My player keeps shooting to right side even though he's facing left.
I've created a target called Firepoint, it's set to as a child Object of the player. I want it to change as the character changes it's direction.
Any help is appreciated.
Here's the code of the player.
public void Move()
{
float controlThrow = CrossPlatformInputManager.GetAxis("Horizontal");
Vector2 playerVelocity = new Vector2(controlThrow * runSpeed, myRigidBody.velocity.y);
myRigidBody.velocity = playerVelocity;
bool playerHasHorizontalSpeed = Mathf.Abs(myRigidBody.velocity.x) > Mathf.Epsilon;
myAnimator.SetBool("Walking", playerHasHorizontalSpeed);
}
public void Flipsprite()
{
bool playerhashorizontalspeed = Mathf.Abs(myRigidBody.velocity.x) > Mathf.Epsilon;
if (playerhashorizontalspeed)
{
transform.localScale = new Vector2(Mathf.Sign(myRigidBody.velocity.x), 1f);
transform.rotation = new Vector2(Mathf.Sign(firePoint.transform.localScale.x), 1f);
}
}
The problem here is that the rotation is not effected by scaling. Only the relative scales and positions.
Also why are you using > Mathf.Epsilon and not simply > 0. As far as I understand you only need to check if it is not 0 ... using Mathf.Epsilon is such a small difference that it actually doesn't matter.
public void Move()
{
float controlThrow = CrossPlatformInputManager.GetAxis("Horizontal");
Vector2 playerVelocity = new Vector2(controlThrow * runSpeed, myRigidBody.velocity.y);
myRigidBody.velocity = playerVelocity;
bool playerHasHorizontalSpeed = Mathf.Abs(myRigidBody.velocity.x) > 0;
myAnimator.SetBool("Walking", playerHasHorizontalSpeed);
}
// store the last direction
int direction;
public void Flipsprite()
{
bool playerhashorizontalspeed = Mathf.Abs(myRigidBody.velocity.x) > 0;
if (playerhashorizontalspeed)
{
// update the direction
direction = Mathf.Sign(myRigidBody.velocity.x);
transform.localScale = new Vector2(direction, 1f);
}
}
Then when you are shooting multiply the direction also with direction
private IEnumerator FireContinuously()
{
while (true)
{
GameObject laser = Instantiate(bullet, firePoint.position, firePoint.rotation);
laser.GetComponent<Rigidbody2D>().velocity = new Vector2(projectileSpeed * direction, 0);
yield return new WaitForSeconds(projectileFiringPeriod);
}
}
a small hint:
If you make the type of
public RigidBody2D bullet
and drag in the according prefab again than you don't need to use GetComponent but could directly use
var laser = Instantiate(bullet, firePoint.position, firePoint.rotation);
laser.velocity = ...