Hello, Unity community!
I am trying to make a game where you can “punch” 2D Rigidbodies to knock them around. I do this by raycasting and adding a force at the hit position. This force goes either straight left or straight right, depending on the direction you punched in.
However, the code has been acting very strange. It always seems to come TOWARDS the player instead of away. Although, SOMETIMES, if it is rotated a certain way and punched in the right spot, it will be pushed away from the player.
Here is the code that needs fixing. The main issue is likely located in Punch() or Update(). If anyone can help figure out the issue and how to use the AddForceAtPosition method properly, that would be very helpful.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum ThresholdState
{
Entered,
In,
Exited,
Out
};
public class Demolitionist : MonoBehaviour
{
public float acceleration;
public float maxSpeed; // No more force will be applied once the max speed has been crossed.
public float turnAcceleration; // If trying to move in the opposite direction you're going, you'll accelerate faster. ONLY IF ON GROUND.
[SerializeField] LayerMask groundLayer;
int groundLayerNumber;
[SerializeField] float jumpStrength= 10.0f;
[SerializeField] float coyoteTime = 0.2f;
float coyoteTimeLeft = 0.0f;
Rigidbody2D rb;
bool grounded = false;
[SerializeField][Range(0.0f, 90.0f)] float maxGroundAngle = 45.0f;
Vector2 groundNormal;
float currentGroundAngle = 0.0f;
int amountGroundCollisions = 0;
CircleCollider2D circleCollider;
[SerializeField] float punchRange = 0.4f;
[SerializeField] float punchForce = 30.0f;
[SerializeField] float punchCooldown = 0.35f;
float punchCooldownLeft = 0.0f;
float currentDirection = -1.0f;
[SerializeField] GameObject fist;
float fistTime = 0.0f;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
circleCollider = this.gameObject.GetComponent<CircleCollider2D>();
groundLayerNumber = LayerMask.NameToLayer("Ground");
Debug.Log("Ground layer: " + groundLayerNumber);
}
Vector2 rotateLeft(Vector2 original)
{
Vector2 newVector = original;
newVector.x = original.y * -1.0f;
newVector.y = original.x;
return newVector;
}
Vector2 rotateRight(Vector2 original)
{
Vector2 newVector = original;
newVector.x = original.y;
newVector.y = original.x * -1.0f;
return newVector;
}
void OnCollisionEnter2D(Collision2D other)
{
if(other.gameObject.layer == groundLayerNumber)
{
++amountGroundCollisions;
Debug.Log(amountGroundCollisions + " collisions");
}
else
{
Debug.Log("Not ground entered.");
Debug.Log(other.gameObject.layer);
}
}
void OnCollisionExit2D(Collision2D other)
{
if (other.gameObject.layer == groundLayerNumber)
{
--amountGroundCollisions;
Debug.Log(amountGroundCollisions + " collisions");
}
else
{
Debug.Log("Not ground exited.");
Debug.Log(other.gameObject.layer);
}
}
void OnCollisionStay2D(Collision2D other)
{
Vector2 averageGroundNormal = new Vector2(0.0f, 0.0f);
int groundPoints = 0;
float minY = Mathf.Sin(maxGroundAngle) / 2.0f;
foreach(ContactPoint2D contact in other.contacts)
{
if(contact.normal.y > minY)
{
groundPoints++;
averageGroundNormal += contact.normal;
}
}
if(groundPoints > 0)
{
groundNormal = averageGroundNormal / groundPoints;
//Debug.Log(Mathf.Atan2(groundNormal.y, groundNormal.x) * Mathf.Rad2Deg);
}
//groundAngleVector = new Vector2(Mathf.Cos(maxGroundAngle * Mathf.Deg2Rad), Mathf.Sin(maxGroundAngle * Mathf.Deg2Rad));
//for(ContactPoint2D contact in other)
//{
// if(contact.point.y <= transform.position.y)
// {
//
// }
//}
}
void Punch(Vector2 direction)
{
RaycastHit2D hit;
hit = Physics2D.Raycast(transform.position, direction, punchRange, groundLayer);
if (hit)
{
if(hit.collider.gameObject.GetComponent<Rigidbody2D>())
{
Rigidbody2D otherRB = hit.collider.gameObject.GetComponent<Rigidbody2D>();
Debug.Log(direction * punchForce);
otherRB.AddForceAtPosition(hit.point, direction * punchForce, ForceMode2D.Impulse);
//otherRB.AddForce(direction * punchForce, ForceMode2D.Impulse);
if (hit.collider.gameObject.GetComponent<BreakablePart>())
{
BreakablePart bp = hit.collider.gameObject.GetComponent<BreakablePart>();
bp.AddHit(new BPHit(hit.point, direction * punchForce, 2.0f));
}
}
}
fistTime = 0.15f;
fist.GetComponent<SpriteRenderer>().enabled = true;
}
Vector2 CalculateGroundNormal(ContactPoint2D[] points)
{
Vector2 averageGroundNormal = new Vector2(0.0f, 0.0f);
int groundPoints = 0;
float minY = Mathf.Sin(maxGroundAngle) / 2.0f;
foreach (ContactPoint2D contact in points)
{
if (contact.normal.y > minY)
{
groundPoints++;
averageGroundNormal += contact.normal;
}
}
if (groundPoints > 0)
{
averageGroundNormal /= groundPoints;
//Debug.Log(Mathf.Atan2(groundNormal.y, groundNormal.x) * Mathf.Rad2Deg);
}
return averageGroundNormal;
}
// Update is called once per frame
/*
void Update()
{
}
*/
void SelfAccelerate(float accel)
{
}
void Accelerate(float accel)
{
rb.AddForce(new Vector2(accel * rb.mass, 0.0f));
//Debug.Log(accel);
}
ThresholdState thresholdState = ThresholdState.Out;
void Update()
{
//Debug.Log(currentDirection);
if(Input.GetKeyDown(KeyCode.A))
{
coyoteTimeLeft = coyoteTime;
}
if(Input.GetKeyDown(KeyCode.S) && punchCooldownLeft <= 0.0f)
{
Punch(new Vector2(currentDirection, 0.0f));
punchCooldownLeft = punchCooldown;
}
if(fistTime > 0.0f)
{
fist.transform.position = transform.position + new Vector3((punchRange - (fist.transform.localScale.x / 2.0f)) * currentDirection, 0.0f, 0.0f);
}
else
fist.GetComponent<SpriteRenderer>().enabled = false;
}
void FixedUpdate()
{
// Check if player entered the threshold of stopping.
//if (Mathf.Abs(rb.velocity.x) <= acceleration * Time.fixedDeltaTime)
// inThreshold = true;
//else if (inThreshold) inThreshold = false;
// Move player
// Ground check
// Perform circle cast below, checking for ground
//RaycastHit2D castHit = Physics2D.CircleCast(transform.position, circleCollider.radius + 0.02f, Vector2.down, Mathf.Infinity, groundLayer);
grounded = amountGroundCollisions > 0;
float inputDirection = Input.GetAxisRaw("Horizontal");
// Choose direction.
if(Mathf.Approximately(inputDirection, 0.0f)){}
else if (inputDirection > 0.0f)
currentDirection = 1.0f;
else
currentDirection = -1.0f;
rb.AddForce(groundNormal * -2.0f);
if (grounded && coyoteTimeLeft > 0.0f)
{
rb.AddForce(new Vector2(0.0f, jumpStrength * rb.mass), ForceMode2D.Impulse);
coyoteTimeLeft = 0.0f;
}
if (inputDirection > 0.0f && rb.velocity.x < maxSpeed)
{
rb.AddForce(new Vector2(acceleration * rb.mass, 0.0f));
/*
if(grounded)
{
Vector2 movementNormal = rotateRight(groundNormal);
//Debug.Log("Movement normal: " + movementNormal.x + ", "+ movementNormal.y);
rb.AddForce(new Vector2(acceleration * movementNormal.x * rb.mass, acceleration * movementNormal.y * rb.mass));
}
else
{
rb.AddForce(new Vector2(acceleration * rb.mass, 0.0f));
}
*/
}
else if(inputDirection < 0.0f && rb.velocity.x > maxSpeed * -1.0f)
{
rb.AddForce(new Vector2(acceleration * -1.0f * rb.mass, 0.0f));
/*
if (grounded)
{
Vector2 movementNormal = rotateLeft(groundNormal);
//Debug.Log("Movement normal: " + movementNormal.x + ", "+ movementNormal.y);
rb.AddForce(new Vector2(acceleration * movementNormal.x * rb.mass, acceleration * movementNormal.y * rb.mass));
}
else
{
rb.AddForce(new Vector2(acceleration * -1.0f * rb.mass, 0.0f));
}
*/
}
coyoteTimeLeft -= Time.fixedDeltaTime;
if (fistTime > 0.0f)
{
fistTime -= Time.fixedDeltaTime;
}
if(punchCooldown > 0.0f)
{
punchCooldownLeft -= Time.fixedDeltaTime;
}
}
}