I would like to add a force to my ball in my golf game, so if the player clicks the ball and holds a left mouse button, the power of the shot is growing, so is the trajectory. When the power reaches a maximum value the ball is shot immediately. I have a huge problem with it, and I'm stuck on it for a few days now. Maybe someone can help me? Code below:
internal Rigidbody2D ballRB;
internal Vector3 startPos = Vector3.zero;
int dotCount;
internal bool isClicked = false;
bool trajectoryVisible = false;
bool paraboleReachedEndOfScreen = true;
GameObject trajectoryDots;
GameObject ballClick;
//TrajectoryDots
List<Transform> dots = new List<Transform>();
//shot velocity
Vector2 shotForce = Vector2.zero;
public float startValShootingPower = 3f;
public float shootingPower = 3f;
float minBallPointerDist = 0.5f;
float dotSeparation = 10f;
float dotShift = 5f;
Transform lastDotTransform;
void Start()
{
startPos = transform.position;
startValShootingPower = shootingPower;
ballRB = GetComponent<Rigidbody2D>();
trajectoryDots = GameObject.Find("Trajectory Dots");
ballClick = transform.Find("Ball Click Area").gameObject;
dotCount = trajectoryDots.transform.childCount;
foreach (var dot in trajectoryDots.GetComponentsInChildren<Transform>())
{
dots.Add(dot);
}
dots.Remove(trajectoryDots.transform);
}
void Update()
{
foreach (var dot in dots)
{
if (dot.GetComponent<SpriteRenderer>().enabled)
{
lastDotTransform = dot;
}
}
paraboleReachedEndOfScreen = lastDotTransform.position.x <= -UsefulReferences.CameraViewFrustum.x / 2 || lastDotTransform.position.x >= UsefulReferences.CameraViewFrustum.x / 2;
ballClick.SetActive(!isClicked);
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (Input.GetButtonDown("Fire1"))
{
if (hit.collider != null)
{
//check if the mouse is hovering ball click area
if (hit.collider.transform.root.name == name)
{
trajectoryVisible = true;
}
}
}
if ((Input.GetButtonUp("Fire1") || (paraboleReachedEndOfScreen && Input.GetButton("Fire1"))) && !isClicked && trajectoryVisible) //player released the mouse button or the parabole reached the end of the screen
{
trajectoryVisible = false;
isClicked = true;
UsefulReferences.ballScript.ballRB.constraints = RigidbodyConstraints2D.FreezeRotation;
ballRB.velocity = shotForce;
foreach (var dot in dots)
dot.transform.position = Vector3.zero;
Invoke("GroundCheckAfterShot", 0.1f);
}
if (trajectoryVisible && !isClicked)
{
shotForce = (transform.position - Camera.main.ScreenToWorldPoint(Input.mousePosition)) * shootingPower;
for (int i = 0; i < dotCount; i++)
{
dots[i].gameObject.SetActive(true);
dots[i].transform.position = new Vector2(transform.position.x + shotForce.x * Time.fixedDeltaTime * (dotSeparation * i + dotShift),
transform.position.y + shotForce.y * Time.fixedDeltaTime * (dotSeparation * i + dotShift) - (-Physics2D.gravity.y / 2f * Time.fixedDeltaTime * Time.fixedDeltaTime * (dotSeparation * i + dotShift) * (dotSeparation * i + dotShift)));
}
}
else
{
foreach (var dot in dots)
{
dot.gameObject.SetActive(false);
}
}
//if the ball tried to fly out of the screen
if (transform.position.y > UsefulReferences.CameraViewFrustum.y / 2 || transform.position.x > UsefulReferences.CameraViewFrustum.x / 2 || transform.position.x < -UsefulReferences.CameraViewFrustum.x / 2)
{
OnCollisionEnter2D(null);
}
if (isClicked && UsefulReferences.ballScript.ballRB.constraints == RigidbodyConstraints2D.FreezeAll)
{
UsefulReferences.gcsScript.gameOver = true;
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Hole")
{
UsefulReferences.gcsScript.Score();
ballRB.constraints = RigidbodyConstraints2D.FreezeAll;
}
}
void OnCollisionEnter2D(Collision2D collision)
{
ballRB.constraints = RigidbodyConstraints2D.FreezeAll;
}
void GroundCheckAfterShot()
{
if (transform.position.y - startPos.y < 0.05f)
ballRB.constraints = RigidbodyConstraints2D.FreezeAll;
}
}
Related
So I have made a Trajectory for my ball.
This is the code...
{
[Header("Line renderer veriables")]
public LineRenderer line;
[Range(2, 30)]
public int resolution;
Rigidbody rb;
[Header("Formula variables")]
public Vector3 velocity;
public float yLimit;
private float g;
[Header("Linecast variables")]
[Range(2, 30)]
public int linecastResolution;
public LayerMask canHit;
public Rigidbody ballRigid;
private void Start()
{
rb = GetComponent<Rigidbody>();
g = Mathf.Abs(Physics.gravity.y);
}
private void Update()
{
RenderArc();
}
private void RenderArc()
{
line.positionCount = resolution + 1;
line.SetPositions(CalculateLineArray());
}
private Vector3[] CalculateLineArray()
{
Vector3[] lineArray = new Vector3[resolution + 1];
var lowestTimeValueX = MaxTimeX() / resolution;
var lowestTimeValueZ = MaxTimeZ() / resolution;
var lowestTimeValue = lowestTimeValueX > lowestTimeValueZ ? lowestTimeValueZ : lowestTimeValueX;
for (int i = 0; i < lineArray.Length; i++)
{
var t = lowestTimeValue * i;
lineArray[i] = CalculateLinePoint(t);
}
return lineArray;
}
private Vector3 HitPosition()
{
var lowestTimeValue = MaxTimeY() / linecastResolution;
for (int i = 0; i < linecastResolution + 1; i++)
{
RaycastHit rayHit;
var t = lowestTimeValue * i;
var tt = lowestTimeValue * (i + 1);
if (Physics.Linecast(CalculateLinePoint(t), CalculateLinePoint(tt), out rayHit, canHit))
return rayHit.point;
}
return CalculateLinePoint(MaxTimeY());
}
private Vector3 CalculateLinePoint(float t)
{
float x = rb.velocity.x * t;
float z = rb.velocity.z * t;
float y = (rb.velocity.y * t) - (g * Mathf.Pow(t, 2) / 2);
return new Vector3(x + transform.position.x, y + transform.position.y, z + transform.position.z);
}
private float MaxTimeY()
{
var v = rb.velocity.y;
var vv = v * v;
var t = (v + Mathf.Sqrt(vv + 2 * g * (transform.position.y - yLimit))) / g;
return t;
}
private float MaxTimeX()
{
if (IsValueAlmostZero(velocity.x))
SetValueToAlmostZero(ref velocity.x);
var x = rb.velocity.x;
var t = (HitPosition().x - transform.position.x) / x;
return t;
}
private float MaxTimeZ()
{
if (IsValueAlmostZero(velocity.z))
SetValueToAlmostZero(ref velocity.z);
var z = rb.velocity.z;
var t = (HitPosition().z - transform.position.z) / z;
return t;
}
private bool IsValueAlmostZero(float value)
{
return value < 0.0001f && value > -0.0001f;
}
private void SetValueToAlmostZero(ref float value)
{
value = 0.0001f;
}
public void SetVelocity(Vector3 velocity)
{
this.velocity = velocity;
}
}
I have a slingshot With a objectholder(which holds the ball). Also I have a robot arm that pulls the objectholder with its clawhand . The arm pulls it but The ball is in kinematic until the claw opens. The objectholder contains springs joints so its able to go back to its place and make a elastic effect and throw the ball. Now with this code when I pull the trajectory doesnt appear until i let go and it picks a target by itself and just follows the line. What i want is when I pull it the trajectory shows and the user is able to pick the target or move the trajectory. Note: (If i get rid of rb.velocity and replace it with velocity, then when i pull it it shows the trajectory and I can change the target but when i open my claw and the ball shoots, it doesnt follow the trajectory, its like the trajectory follows the ball. Can someone helps? This code makes the ball follows the line so its good I just have that problem.
My other code when clawhand opens and closes and ball is kinematic or not:
public class open2close : MonoBehaviour
{
public float speed;
private Animation anim;
[SerializeField] bool isClosed;
private bool isKinematic;
public Transform rightClaw;
public Vector3 offset;
public Rigidbody Rb2;
public Rigidbody ball_Rigid;
void Start()
{ Debug.Log(transform.position);
Console.WriteLine("test");
anim = gameObject.GetComponent<Animation>();
Rb2 = Rb2.GetComponent<Rigidbody>();
ball_Rigid = ball_Rigid.GetComponent<Rigidbody>();
//Rb2.isKinematic = true;
}
void Update()
{
// Debug.Log(transform.position);
if (Input.GetKey(KeyCode.X))
{
anim.Play("clawopen");
isClosed = false;
Rb2.isKinematic = false;
ball_Rigid.isKinematic = false;
}
if (Input.GetKey(KeyCode.Y))
{
anim.Play("clawclose");
isClosed = true;
Rb2.isKinematic = true;
ball_Rigid.isKinematic = true;
}
}
private void OnTriggerStay(Collider spring2)
{
Rigidbody colRb = spring2.attachedRigidbody;
if (isClosed && spring2.transform.gameObject.tag == "Player")
{
spring2.transform.position = rightClaw.transform.position + offset;
}
}
private void OnTriggerExit(Collider spring2)
{
Rigidbody colRb = spring2.attachedRigidbody;
if (isClosed && spring2.transform.gameObject.tag == "Player")
{
colRb.WakeUp();
}
}
}
I just finished a BreakOut style game but there is a bug where sometimes the ball gets stuck to the edges of the map with no direction or speed as shown in the screenshot
What I see is that it happens when the ball completely loses trajectory or speed, but could not solve the error
enter image description here
my code
public class Ball : MonoBehaviour
{
[SerializeField] Rigidbody2D rigidbody2D;
Vector2 moveDirection;
Vector2 currentVelocity;
float velocity=10;
//GameManager gameManager;
Transform paddle;
[SerializeField] AudioController audioController;
[SerializeField] AudioClip bounceSfx;
[SerializeField] AudioClip dieSfx;
public bool superBall;
[SerializeField] float superBallTime=10;
[SerializeField]float yMinSpeed = 10;
[SerializeField]TrailRenderer trailRenderer;
public bool SuperBall
{
get=> superBall;
set{
superBall=value;
if(superBall)
StartCoroutine(ResetSuperBall());
}
}
// Start is called before the first frame update
void Start()
{
//rigidbody2D = GetComponent<Rigidbody2D>();
//rigidbody2D.velocity = Vector2.up*velocity*Time.deltaTime;
GameManager.Instance = FindObjectOfType<GameManager>();
paddle = transform.parent;
}
// Update is called once per frame
void Update()
{
currentVelocity = rigidbody2D.velocity;
if (Mathf.Abs(currentVelocity.y) < 3 && Mathf.Abs(currentVelocity.y) < 3 && GameManager.Instance.ballOnGame)
{
velocity = 10;
rigidbody2D.velocity = Vector2.up * velocity ;
}
if (Mathf.Abs(currentVelocity.y) + Mathf.Abs(currentVelocity.y) < 10 && GameManager.Instance.ballOnGame)
{
velocity = 10;
rigidbody2D.velocity = Vector2.up * velocity ;
}
if (velocity <10 && GameManager.Instance.ballOnGame)
{
velocity = 10;
rigidbody2D.velocity = Vector2.up * velocity ;
}
if ((Input.GetKey(KeyCode.W) && GameManager.Instance.ballOnGame == false)||(Input.GetKey(KeyCode.Space) && GameManager.Instance.ballOnGame == false))
{
rigidbody2D.velocity = Vector2.up * velocity ;
transform.parent = null;
GameManager.Instance.ballOnGame = true;
rigidbody2D.isKinematic = false;
rigidbody2D.AddForce(new Vector3(velocity, velocity, 0));
if (!GameManager.Instance.GameStarted)
{
GameManager.Instance.GameStarted = true;
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.transform.CompareTag("Brick") && superBall)
{
rigidbody2D.velocity = currentVelocity;
return;
}
moveDirection=Vector2.Reflect(currentVelocity,collision.GetContact(0).normal);
if (Mathf.Abs(moveDirection.y) < yMinSpeed)
{
//permitir velocidad minima
moveDirection.y = yMinSpeed*Mathf.Sign(moveDirection.y);
}
rigidbody2D.velocity=moveDirection;
audioController.PlaySfx(bounceSfx);
if (collision.transform.CompareTag("BottomLimit"))
{
if(GameManager.Instance != null)
{
GameManager.Instance.PlayerLives--;
audioController.PlayDie(dieSfx);
if (GameManager.Instance.PlayerLives > 0)
{
rigidbody2D.velocity = Vector2.zero;
transform.SetParent(paddle);
transform.localPosition = new Vector2(0, 0.65f);
GameManager.Instance.ballOnGame = false;
}
}
}
}
IEnumerator ResetSuperBall()
{
trailRenderer.enabled = true;
yield return new WaitForSeconds(superBallTime);
trailRenderer.enabled = false;
GameManager.Instance.powerIsActive = false;
superBall = false;
}
}
This is a common issue with Rigidbodies at low velocities. The physics engine implements a Physics2D.velocityThreshold that is designed to dampen slow bounces and calm down a pile of Rigidbodies.
The default value for velocityThreshold is 1, meaning that the x or y component of the velocity that is slower will be floored to 0.
To resolve this issue you can simply lower the value to 0.001. You can do it in the Physcs2d tab located at Edit->ProjectSettings->Physics2d
I was following a tutorial but I'm having trouble with the CheckCollisionOverlap() function. It's meant to check for a collision over the player's head when the player is crouching, but for some reason he won't stand back up (or stop walking in place).
If I comment out PhysicsUpdate() and CheckCollisionOverlap(), he will crouch, walk crouch, and stand up from crouch, so I know the problem has to exist within that logic.
In the tutorial comments section, someone mentioned that they changed the value of the layermask. I named a layermask Player and assigned it to layer 8 but that still didn't do anything.
Tutorial Link https://www.youtube.com/watch?v=QVxvwRAYeHU
using UnityEngine;
public class CrouchingState : State
{
float playerSpeed;
bool belowCeiling;
bool crouchHeld;
bool grounded;
float gravityValue;
Vector3 currentVelocity;
public CrouchingState(Character _character, StateMachine _stateMachine):base(_character, _stateMachine)
{
character = _character;
stateMachine = _stateMachine;
}
public override void Enter()
{
base.Enter();
character.animator.SetTrigger("crouch");
belowCeiling = false;
crouchHeld = false;
gravityVelocity.y = 0;
playerSpeed = character.crouchSpeed;
character.controller.height = character.crouchColliderHeight;
character.controller.center = new Vector3(0f, character.crouchColliderHeight / 2f, 0f);
grounded = character.controller.isGrounded;
gravityValue = character.gravityValue;
}
public override void Exit()
{
base.Exit();
character.controller.height = character.normalColliderHeight;
character.controller.center = new Vector3(0f, character.normalColliderHeight / 2f, 0f);
gravityVelocity.y = 0f;
character.playerVelocity = new Vector3(input.x, 0, input.y);
character.animator.SetTrigger("move");
}
public override void HandleInput()
{
base.HandleInput();
if (crouchAction.triggered && !belowCeiling)
{
crouchHeld = true;
}
input = moveAction.ReadValue<Vector2>();
velocity = new Vector3(input.x, 0, input.y);
velocity = velocity.x * character.cameraTransform.right.normalized + velocity.z * character.cameraTransform.forward.normalized;
velocity.y = 0f;
}
public override void LogicUpdate()
{
base.LogicUpdate();
character.animator.SetFloat("speed", input.magnitude, character.speedDampTime, Time.deltaTime);
if (crouchHeld)
{
stateMachine.ChangeState(character.standing);
}
}
public override void PhysicsUpdate()
{
base.PhysicsUpdate();
belowCeiling = CheckCollisionOverlap(character.transform.position + Vector3.up * character.normalColliderHeight);
gravityVelocity.y += gravityValue * Time.deltaTime;
grounded = character.controller.isGrounded;
if (grounded && gravityVelocity.y < 0)
{
gravityVelocity.y = 0f;
}
currentVelocity = Vector3.Lerp(currentVelocity, velocity, character.velocityDampTime);
character.controller.Move(currentVelocity * Time.deltaTime * playerSpeed + gravityVelocity * Time.deltaTime);
if (velocity.magnitude > 0)
{
character.transform.rotation = Quaternion.Slerp(character.transform.rotation, Quaternion.LookRotation(velocity), character.rotationDampTime);
}
}
public bool CheckCollisionOverlap(Vector3 targetPositon)
{
int layerMask = 1 << 8;
layerMask = ~layerMask;
RaycastHit hit;
Vector3 direction = targetPositon - character.transform.position;
if (Physics.Raycast(character.transform.position, direction, out hit, character.normalColliderHeight, layerMask))
{
Debug.DrawRay(character.transform.position, direction * hit.distance, Color.yellow);
return true;
}
else
{
Debug.DrawRay(character.transform.position, direction * character.normalColliderHeight, Color.white);
return false;
}
}
}
I had to change all the layers of the children in the player gameobject as well. Not really sure why that worked, but its responding.
I've been asking so many Unity questions in such little time and I really appreciate all those who answer. I don't know why I always face such issues for which any of the answers I see on the net aren't able to help me out. Okay so here's my problem:
if (Input.GetKey(KeyCode.Q) && (rightHit.collider != null || leftHit.collider != null) && groundHit.collider == null)
This line of code here in the Update function checks every frame if I'm holding down the Q button on any wall. If the condition is satisfied, then
void Grapple()
{
if (wallGrapple)
{
x = 0f;
rb.gravityScale = 0;
rb.velocity = new Vector2(speed * x, speed * y);
if (Input.GetKeyDown(KeyCode.Space)) { jumpRequest = true; }
}
}
this function is called which allows the player to stick and slide on the wall based on Vertical axis input(which feeds the y variable). Now the problem is that if I only press Q once on a wall then it stays on the wall even though its supposed to be held down for Grapple() to be called [I've used Input.GetKey instead of Input.GetKeyDown] and I am also able to slide above the wall (like there's some sorta invisible wall) even though in the if line, the condition for detecting a wall is being checked.
I've been at it for around 30-45 minutes and I'm unable to crack it. Another workaround I thought of implementing was using a coroutine but I don't know how to use as I haven't found any basic explanation of how it works and how its supposed to be used. Here's the full code,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SocialPlatforms;
public class Move : MonoBehaviour
{
[SerializeField] private LayerMask groundMask;
[SerializeField] private LayerMask Wall;
[Header("Horizontal")]
public float x;
float dirX;
public float speed;
float initialSpeed;
[SerializeField] [Range(0, 1)] float LerpConstant;
public float dashForce = 100f;
public bool canDash;
public bool dashRequest;
[Header("Vertical")]
public float y;
public bool canJump;
public bool jumpRequest;
public float initialJumpForce;
public float jumpForce;
public bool canWallJump;
public float modifiedJumpForce;
public float sideJumpForce;
public bool wallGrapple;
[Header("Other")]
public Rigidbody2D rb;
public float gScale = 10f;
BoxCollider2D boxcollider;
RaycastHit2D groundHit;
RaycastHit2D leftHit;
RaycastHit2D rightHit;
// Start is called before the first frame update
void Start()
{
rb = gameObject.GetComponent<Rigidbody2D>();
boxcollider = gameObject.GetComponent<BoxCollider2D>();
jumpForce = initialJumpForce;
initialSpeed = speed;
rb.gravityScale = gScale;
}
// Update is called once per frame
void Update()
{
//Instant 1, 0, -1 inputs
x = Input.GetAxisRaw("Horizontal");
y = Input.GetAxisRaw("Vertical");
if(x < 0)
{ dirX = -1; }
else if(x > 0)
{ dirX = 1; }
if (Input.GetKeyDown(KeyCode.Space) && (canJump || canWallJump)) //for jumps
{ jumpRequest = true; }
if (Input.GetKey(KeyCode.Q) && (rightHit.collider != null || leftHit.collider != null) && groundHit.collider == null) //for wall grappling
{ wallGrapple = true; }
if (Input.GetKeyDown(KeyCode.LeftShift) && (canDash)) //for dashing
{ dashRequest = true; }
Detection();
JumpDash();
Grapple();
}
void FixedUpdate()
{
Movement();
}
void Detection()
{
groundHit = Physics2D.BoxCast(boxcollider.bounds.center, boxcollider.bounds.size, 0f, Vector2.down, 0.1f, groundMask);
if (groundHit.collider != null) //If player is on ground
{
canJump = true;
canWallJump = false;
canDash = false;
jumpForce = initialJumpForce;
sideJumpForce = 0;
LerpConstant = 0.25f;
}
else if (groundHit.collider == null)//not on ground
{
LerpConstant = 0.12f;
canJump = false;
}
//Wall detection
//Left wall
leftHit = Physics2D.BoxCast(boxcollider.bounds.center, boxcollider.bounds.size, 0f, Vector2.left, 0.1f, Wall);
if (leftHit.collider != null && groundHit.collider == null) //if player on left wall and not on ground
{
canWallJump = true;
jumpForce = modifiedJumpForce;
sideJumpForce = jumpForce;
}
//Right wall
rightHit = Physics2D.BoxCast(boxcollider.bounds.center, boxcollider.bounds.size, 0f, Vector2.right, 0.1f, Wall);
if (rightHit.collider != null && groundHit.collider == null) //if player on right wall and not on ground
{
canWallJump = true;
jumpForce = modifiedJumpForce;
sideJumpForce = -jumpForce; // negative of jump force to jump in left direction
}
if (rightHit.collider == null && leftHit.collider == null) //if both walls are not detected
{
canWallJump = false;
}
}
void Movement()
{
Vector2 move = new Vector2(x * speed, rb.velocity.y);
rb.velocity = Vector2.Lerp(rb.velocity, move, LerpConstant);
}
void JumpDash()
{
//Jump
if (jumpRequest)
{
wallGrapple = false;
rb.gravityScale = gScale;
rb.AddForce(new Vector2(sideJumpForce * 2.1f, jumpForce), ForceMode2D.Impulse);
speed = initialSpeed;
jumpRequest = false; //setting jumpRequest to false after jumping to prevent unlimited jumps
canDash = true;
}
/*if (dashRequest)
{
rb.velocity = Vector2.zero;
rb.AddForce((dashForce, 0f), ForceMode2D.Impulse);
}*/ //remember to put this code in later
}
void Grapple()
{
if (wallGrapple)
{
x = 0f;
rb.gravityScale = 0;
rb.velocity = new Vector2(speed * x, speed * y);
if (Input.GetKeyDown(KeyCode.Space)) { jumpRequest = true; }
}
}
}
P.S. I originally wanted to use Space itself instead of Q for the stick and slide but more errors pop up like inconsistent wall jumping so that's why I used Q.
https://streamable.com/3v6qrq <- Video showing the problem.
It seems you haven't set
wallGrapple = false
anywhere in your code except in the jump request. Therefore, as it never gets set to false your player will continuously grapple.
Maybe you can set it to false in the Update function first and do the key detection.
I'm showing you a coroutine example code snippet.
You can understand it from the comment lines and apply it to your own code.
void Start()
{
StartCoroutine("coroutineFunc"); // calling for example
}
and coroutine func like this:
IEnumerator coroutineFunc()
{
// before waiting... if u wanna try something
yield return new WaitForSeconds(1); // waiting..
// after waiting something what do you want
}
I really hope you guys can help me with my problem, this was my last resort and im super frustrated.
I'm creating a 2D Side-Scroller game and while coding the player file, I've ran into a very annoying thing that I don't know where the problem is. Everything else in the player is fine, what happens is that if the player is walking on a slant, and then comes off; the gravity (I'm assuming) is being messed with and when your in the air he just floats down instead of falling; also if you jump (after walking on the slant) he just does a teeeeeeny tiny hop. I have debugged the whole file several times and can't seem to figure it out.
If someone would please help, I'll put the whole player file. If you have a 2D sidescroller game in unity, you can just put this file on a gameobject with a rigidBody2D and isKinemic is true..
using UnityEngine;
using System.Collections;
using System;
public class PlayerControl : MonoBehaviour {
private Animator anim;
public static PlayerControl instance;
public static bool isShooting;
[HideInInspector]
public bool facingRight;
private float normalSpeed;
private static readonly float slopeLimitTangent = Mathf.Tan(75f * Mathf.Deg2Rad);
public float maxSpeed = 8f;
private float speedAccelerationOnGround = 10f;
private float speedAccelerationInAir = 5f;
private Vector2 velocity {get {return vel;}}
public int health = 100;
private bool isDead;
private const float skinWidth = .02f;
private const int horizRays = 8;
private const int vertRays = 4;
public enum JumpBehavior {
JumpOnGround,
JumpAnywhere,
CantJump
};
public JumpBehavior jumpWhere;
private float jumpIn;
public float jumpFreq = 0.25f;
public float jumpMag = 16;
public LayerMask whatIsGround;
private bool grounded { get { return colBelow; } }
private bool cooldown;
public GameObject standingOn {get; private set;}
public Vector3 platformVelocity {get;private set;}
public bool canJump { get {
if (jumpWhere == JumpBehavior.JumpAnywhere)
return jumpIn <= 0;
if (jumpWhere == JumpBehavior.JumpOnGround)
return grounded;
return false;
}
}
public bool colRight { get; set;}
public bool colLeft { get; set;}
public bool colAbove{ get; set;}
public bool colBelow{ get; set;}
public bool upSlope{ get; set;}
public bool downSlope{get;set;}
public float slopeAngle {get;set;}
public bool hasCollisions { get { return colRight || colLeft || colAbove || colBelow; }
}
private float
vertDistanceBetweenRays,
horizDistanceBetweenRays;
private Vector3 raycastTopLeft;
private Vector3 raycastBottomRight;
private Vector3 raycastBottomLeft;
private Vector2 maxVelocity = new Vector2(float.MaxValue,
float.MaxValue);
private Vector2 vel;
[Range(0, 90)]
public float slopeLimit = 30;
public float gravity = -15;
private GameObject lastStandingOn;
private Vector3 activeGlobalPlatformPoint;
private Vector3 activeLocalPlatformPoint;
public static int scene = 0;
void Start () {
instance = this;
anim = GetComponent<Animator> ();
float colliderWidth = GetComponent<BoxCollider2D>().size.x * Mathf.Abs
(transform.localScale.x) - (2 * skinWidth);
horizDistanceBetweenRays = colliderWidth / (vertRays - 1);
float colliderHeight = GetComponent<BoxCollider2D>().size.y * Mathf.Abs
(transform.localScale.y) - (2 * skinWidth);
vertDistanceBetweenRays = colliderHeight / (horizRays - 1);
}
void Update () {
if (!isDead)
HandleInput();
float movementFactor = grounded ? speedAccelerationOnGround : speedAccelerationInAir;
if (isDead)
HorizForce(0);
else
HorizForce(Mathf.Lerp(velocity.x, normalSpeed * maxSpeed, Time.deltaTime * movementFactor));
anim.SetBool("Grounded", grounded);
anim.SetBool("Dead", isDead);
anim.SetFloat("Speed", Mathf.Abs(velocity.x) / maxSpeed);
}
public void LateUpdate() {
jumpIn -= Time.deltaTime;
vel.y += gravity * Time.deltaTime;
Move (vel * Time.deltaTime);
}
public void AddForce(Vector2 force) {
vel += force;
}
public void SetForce(Vector2 force) {
vel = force;
}
public void HorizForce(float x) {
vel.x = x;
}
public void VertForce(float y) {
vel.y = y;
}
public void Jump() {
AddForce(new Vector2(0, jumpMag));
jumpIn = jumpFreq;
anim.SetTrigger("Jump");
}
void HandleInput() {
float h = Input.GetAxis("Horizontal");
normalSpeed = h;
if (h < 0) {
if (!facingRight)
Flip ();
facingRight = true;
} else
if (h > 0) {
if (facingRight)
Flip ();
facingRight = false;
} else
normalSpeed = 0;
if (canJump && Input.GetButtonDown("Jump"))
Jump();
if (Input.GetButton("melee") && !Input.GetButtonDown ("Fire1") && MeleeAttack.canHit) {
anim.SetTrigger ("Attack");
MeleeAttack.canHit = false;
MeleeAttack.instance.cooldown ();
}
}
private void Flip() {
transform.localScale = new Vector3(-transform.localScale.x, transform.localScale.y, transform.localScale.z);
facingRight = transform.localScale.x > 0;
}
void ResetColliders() {
colLeft = false;
colRight = false;
colAbove = false;
colBelow = false;
colLeft = false;
slopeAngle = 0;
}
void Move(Vector2 deltaMove) {
bool wasGrounded = colBelow;
ResetColliders();
HandlePlatforms ();
CalcRayOrigins();
if (deltaMove.y < 0 && wasGrounded)
HandleVerticalSlope(ref deltaMove);
if (Mathf.Abs(deltaMove.x) > 0.001f)
MoveHoriz(ref deltaMove);
MoveVert(ref deltaMove);
//CorrectHorizPlacement(ref deltaMove, true);
//CorrectHorizPlacement(ref deltaMove, false);
transform.Translate(deltaMove, Space.World);
if (Time.deltaTime > 0)
vel = deltaMove / Time.deltaTime;
vel.x = Mathf.Min(vel.x, maxVelocity.x);
vel.y = Mathf.Min(vel.y, maxVelocity.y);
if (upSlope)
vel.y = 0;
if (standingOn != null) {
activeGlobalPlatformPoint = transform.position;
activeLocalPlatformPoint = standingOn.transform.InverseTransformPoint(transform.position);
if (lastStandingOn != standingOn) {
if (lastStandingOn != null)
lastStandingOn.SendMessage("ControllerExit2D", this, SendMessageOptions.DontRequireReceiver);
standingOn.SendMessage("ControllerEnter2D", this, SendMessageOptions.DontRequireReceiver);
lastStandingOn = standingOn;
} else if (standingOn != null)
standingOn.SendMessage("ControllerStay2D", this, SendMessageOptions.DontRequireReceiver);
else if (lastStandingOn != null) {
lastStandingOn.SendMessage("ControllerExit2D", this, SendMessageOptions.DontRequireReceiver);
lastStandingOn = null;
}
}
}
void MoveHoriz(ref Vector2 deltaMove) {
bool goingRight = deltaMove.x > 0;
float rayDistance = Mathf.Abs (deltaMove.x) + skinWidth;
Vector2 rayDirection = goingRight ? Vector2.right : -Vector2.right;
Vector3 rayOrigin = goingRight ? raycastBottomRight : raycastBottomLeft;
for (int i = 0; i < horizRays; i++) {
Vector2 rayVect = new Vector2(rayOrigin.x, rayOrigin.y + (i * vertDistanceBetweenRays));
Debug.DrawRay(rayVect, rayDirection * rayDistance, Color.red);
RaycastHit2D raycastHit = Physics2D.Raycast(rayVect, rayDirection, rayDistance, whatIsGround);
if (!raycastHit) continue;
if (i == 0 && HandleHorizontalSlope(ref deltaMove, Vector2.Angle(raycastHit.normal, Vector2.up), goingRight))
break;
deltaMove.x = raycastHit.point.x - rayVect.x;
rayDistance = Mathf.Abs (deltaMove.x);
if (goingRight) {
deltaMove.x -= skinWidth;
colRight = true;
} else {
deltaMove.x += skinWidth;
colLeft = true;
}
if (rayDistance < skinWidth + .0001f)
break;
}
}
private void HandlePlatforms() {
if (standingOn != null) {
Vector3 newGlobalPlatformPoint = standingOn.transform.TransformPoint(activeLocalPlatformPoint);
Vector3 moveDistance = newGlobalPlatformPoint - activeGlobalPlatformPoint;
if (moveDistance != Vector3.zero)
transform.Translate(moveDistance, Space.World);
platformVelocity = (newGlobalPlatformPoint - activeGlobalPlatformPoint) / Time.deltaTime;
} else
platformVelocity = Vector3.zero;
standingOn = null;
}
private void MoveVert(ref Vector2 deltaMovement) {
bool isGoingUp = deltaMovement.y > 0;
float rayDistance = Mathf.Abs(deltaMovement.y) + skinWidth;
Vector2 rayDirection = isGoingUp ? Vector2.up : -Vector2.up;
Vector2 rayOrigin = isGoingUp ? raycastTopLeft : raycastBottomLeft;
rayOrigin.x += deltaMovement.x;
float standingOnDistance = float.MaxValue;
for (int i = 0; i < vertRays; i++) {
Vector2 rayVector = new Vector2(rayOrigin.x + (i * horizDistanceBetweenRays), rayOrigin.y);
Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red);
RaycastHit2D rayCastHit = Physics2D.Raycast(rayVector, rayDirection, rayDistance, whatIsGround);
if (!rayCastHit)
continue;
if (!isGoingUp) {
float verticalDistanceToHit = transform.position.y - rayCastHit.point.y;
if (verticalDistanceToHit < standingOnDistance) {
standingOnDistance = verticalDistanceToHit;
standingOn = rayCastHit.collider.gameObject;
}
}
deltaMovement.y = rayCastHit.point.y - rayVector.y;
rayDistance = Mathf.Abs(deltaMovement.y);
if (isGoingUp) {
deltaMovement.y -= skinWidth;
colAbove = true;
} else {
deltaMovement.y += skinWidth;
colBelow = true;
}
if (!isGoingUp && deltaMovement.y > .0001f)
upSlope = true;
if (rayDistance < skinWidth + .0001f)
break;
}
}
private void HandleVerticalSlope(ref Vector2 deltaMove) {
float center = (raycastBottomLeft.x + raycastBottomRight.x) / 2;
Vector2 direction = -Vector2.up;
float slopeDistance = slopeLimitTangent + (raycastBottomRight.x - center);
Vector2 slopeRayVector = new Vector2(center, raycastBottomLeft.y);
Debug.DrawRay(slopeRayVector, direction * slopeDistance, Color.yellow);
RaycastHit2D rayCastHit = Physics2D.Raycast(slopeRayVector, direction, slopeDistance, whatIsGround);
if (!rayCastHit)
return;
float angle = Vector2.Angle(rayCastHit.normal, Vector2.up);
if (Mathf.Abs (angle) < .0001f)
return;
downSlope = true;
slopeAngle = angle;
deltaMove.y = rayCastHit.point.y - slopeRayVector.y;
transform.rotation = Quaternion.Euler (0, 0, facingRight ? angle : -angle);
}
private bool HandleHorizontalSlope(ref Vector2 deltaMove, float angle, bool isGoingRight) {
if (Mathf.RoundToInt(angle) == 90)
return false;
if (angle > slopeLimit) {
deltaMove.x = 0;
return true;
}
if (deltaMove.y > .07f)
return true;
deltaMove.x += isGoingRight ? -skinWidth : skinWidth;
deltaMove.y = Mathf.Abs(Mathf.Tan(angle * Mathf.Deg2Rad) * deltaMove.x);
upSlope = true;
colBelow = true;
return true;
}
void CalcRayOrigins() {
Vector2 size = new Vector2(GetComponent<BoxCollider2D>().size.x * Mathf.Abs(transform.localScale.x), GetComponent<BoxCollider2D>().size.y * Mathf.Abs(transform.localScale.y)) / 2;
Vector2 center = new Vector2(GetComponent<BoxCollider2D>().center.x * transform.localScale.x, GetComponent<BoxCollider2D>().center.y * transform.localScale.y);
raycastTopLeft = transform.position + new Vector3(center.x - size.x + skinWidth, center.y + size.y - skinWidth);
raycastBottomRight = transform.position + new Vector3(center.x + size.x - skinWidth, center.y - size.y + skinWidth);
raycastBottomLeft = transform.position + new Vector3(center.x - size.x + skinWidth, center.y - size.y + skinWidth);
}
void CorrectHorizPlacement(ref Vector2 deltaMove, bool isRight) {
float halfWidth = (GetComponent<BoxCollider2D> ().size.x * transform.localScale.x) / 2f;
Vector3 rayOrigin = isRight ? raycastBottomRight : raycastBottomLeft;
if (isRight)
rayOrigin.x -= (halfWidth + skinWidth);
else
rayOrigin.x += (halfWidth + skinWidth);
Vector2 rayDirection = isRight ? Vector2.right : -Vector2.right;
float offset = 0;
for (int i = 1; i < horizRays - 1; i++) {
Vector2 rayVector = new Vector2(deltaMove.x + rayOrigin.x, deltaMove.y + rayOrigin.y + (i * vertDistanceBetweenRays));
RaycastHit2D raycastHit = Physics2D.Raycast(rayVector, rayDirection, halfWidth, whatIsGround);
if (!raycastHit) continue;
offset = isRight ? ((raycastHit.point.x - transform.position.x) - halfWidth) : (halfWidth - (transform.position.x - raycastHit.point.x));
deltaMove.x += offset;
}
}
}
please someone help.