i have some problems with simple ballistics.As i am new to coding and i wasted 2 days with searching, i decided to ask a question. I have a problem with my bullet.cs script, code works fine, except ricochet part. I used OnTriggerEnter beacuse bullets will penetrate different materials, but will bounce under specific angles, so i used collider as a trigger only. I know it can be dobe using OnCollisionEnter to determine normal and use Vector3.Reflect. But if collider is not set as a trigger only, bullets will be bouncing off from everithing a will never penetrate a wall. Just need a help with ricochet part on the bottom.
public Rigidbody rb;
private float vel;
private float kEnergy;
private bool AP = false;
private bool HP = false;
private bool ricochet = false;
// Use this for initialization
void Start () {
vel = 738f;
kEnergy = 2108f;
//HP == false;
Destroy (gameObject, 10);
rb = GetComponent<Rigidbody> ();
rb.velocity = transform.forward * vel;
Quaternion bulletRotaion = transform.rotation;
Debug.Log (bulletRotaion.eulerAngles);
}
// Update is called once per frame
void Update () {
float curVelocity = rb.velocity.magnitude;
if (curVelocity <= 0)
{
curVelocity = 0f;
}
}
void OnTriggerEnter (Collider c)
{
if (c.tag == "Wall") {
Quaternion localOffset = transform.rotation;
float impactAngleX = c.gameObject.GetComponent <MaterialDensity> ().angleX;
float impactAngleY = c.gameObject.GetComponent <MaterialDensity> ().angleY;
float impactAngleZ = c.gameObject.GetComponent <MaterialDensity> ().angleZ;
float angleX = localOffset.eulerAngles.x;
float angleY = localOffset.eulerAngles.y;
float angleZ = localOffset.eulerAngles.z;
float density = c.gameObject.GetComponent <MaterialDensity> ().materialDensity;
float ricochetX = (angleX + impactAngleX);
if (ricochetX > 360)
{
ricochetX = (ricochetX - 360);
}
if (ricochetX < 0)
{
ricochetX = (ricochetX + 360);
}
float ricochetY = (angleY + impactAngleY);
if (ricochetY > 360)
{
ricochetY = (ricochetY - 360);
}
if (ricochetY < 0)
{
ricochetY = (ricochetY + 360);
}
float ricochetZ = (angleZ + impactAngleZ);
if (ricochetZ > 360)
{
ricochetZ = (ricochetZ - 360);
}
if (ricochetZ < 0)
{
ricochetZ = (ricochetZ + 360);
}
if ((ricochetX > 60 && ricochetX < 300) || (ricochetY > 60 && ricochetY < 300) || (ricochetZ > 60 && ricochetZ < 300)) {
ricochet = true;
//Debug.Log (ricochet);
}
if ((ricochetX < 60 && ricochetX > 300) || (ricochetY < 60 && ricochetY > 300) || (ricochetZ < 60 && ricochetZ > 300)) {
ricochet = false;
//Debug.Log (ricochet);
}
Debug.Log (ricochet);
Debug.Log (ricochetX);
Debug.Log (ricochetY);
Debug.Log (ricochetZ);
if (AP)
{
density *= 0.9f;
}
if (HP)
{
density *= 3f;
}
//float vel = gameObject.GetComponent<GunNew>().velocity;
float curVelocity = rb.velocity.magnitude;
float velocityMod = curVelocity / vel;
float densityMod = density / velocityMod;
float curEnergy = (kEnergy * velocityMod);
float energyMod = (curEnergy * velocityMod);
rb = GetComponent<Rigidbody> ();
//Quaternion localOffset = transform.rotation;
float randomX = Random.Range (0.03f, 0f);
float randomY = Random.Range (0.03f, -0.03f);
if (randomX == 0)
{
randomX = 0.01f;
}
if (randomY == 0)
{
randomY = 0.01f;
}
localOffset.x += randomX;
localOffset.y += randomY;
if (curVelocity > densityMod && !ricochet) {
rb.rotation = localOffset;
rb.velocity = transform.forward * ((curVelocity - (curVelocity * (randomX + randomY))) - densityMod);
//Debug.Log (curVelocity);
//Debug.Log (energyMod);
}
if (curVelocity > densityMod && ricochet)
{
Vector3 objAngle = new Vector3 (impactAngleX, impactAngleY, impactAngleZ);
Vector3 bulletAngle = rb.rotation.eulerAngles;
Debug.Log (objAngle);
}
if (curVelocity <= densityMod)
{
//Debug.Log (curVelocity);
//Debug.Log (energyMod);
Destroy (gameObject);
}
}
}
second code is a code from wall.
public float materialDensity = 100f;
public float angleX;
public float angleY;
public float angleZ;
//Quaternion localOffset = transform.rotation;
// Use this for initialization
public void Start ()
{
GetComponent<Rigidbody> ();
Quaternion localOffset = transform.rotation;
float angleX = localOffset.eulerAngles.x;
float angleY = localOffset.eulerAngles.y;
float angleZ = localOffset.eulerAngles.z;
//Debug.Log (localOffset.eulerAngles.x);
}
// Update is called once per frame
void Update () {
}
Firstly, I would make a layers in your game for things that will ricochet, and things that don't.
Then when you test for collisions with your bullet you can only check for collisions with the ricochet layer. This will increase performance and also make coding much simpler.
From there you could rely on unity's physics to do it's own ricochet, or if you wanted to calculate it yourself you could do a raycast and use the raycast's normal as described above.
if(Physics.Raycast (shootRay, out shootHit, range, ricochetMask)) {
...
Related
I wanted to add Guns with recoil to my 3d fps game. It worked but for some reason all the time when i look completely downwards i walk backwards or frontwards. This only happens when the gun is active.
The Gun Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunScript : MonoBehaviour
{
[SerializeField] float Recoil = 500f;
[SerializeField] float TimeTillNextShoot;
[SerializeField] float Range = 100f;
[SerializeField] Transform GunTipposition;
[SerializeField] float recoilRadius = 100f;
public Camera PlayerCamera;
public Rigidbody rbPlayer;
private void Update()
{
Shoot();
}
public void Shoot()
{
if (Input.GetButtonDown("Fire1"))
{
RaycastHit hit;
if(Physics.Raycast(PlayerCamera.transform.position, PlayerCamera.transform.forward, out hit, Range))
{
Debug.Log(hit.transform.name);
Enemy Enemytarget = hit.transform.GetComponent<Enemy>();
if(Enemytarget != null)
{
Enemytarget.TakeDamage(true);
}
}
if (gameObject.CompareTag("ShotGun"))
{
Transform RecoilPosition = GunTipposition.transform;
rbPlayer.AddExplosionForce(Recoil, RecoilPosition.position, recoilRadius);
}
}
}
}
The player Movement:
// Some stupid rigidbody based movement by Dani
using System;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[Header("Assignables")]
[Tooltip("this is a reference to the MainCamera object, not the parent of it.")]
public Transform playerCam;
[Tooltip("reference to orientation object, needed for moving forward and not up or something.")]
public Transform orientation;
[Tooltip("LayerMask for ground layer, important because otherwise the collision detection wont know what ground is")]
public LayerMask whatIsGround;
private Rigidbody rb;
[Header("Rotation and look")]
private float xRotation;
[Tooltip("mouse/look sensitivity")]
public float sensitivity = 50f;
private float sensMultiplier = 1.5f;
[Header("Movement")]
[Tooltip("additive force amount. every physics update that forward is pressed, this force (multiplied by 1/tickrate) will be added to the player.")]
public float moveSpeed = 4500;
[Tooltip("maximum local velocity before input is cancelled")]
public float maxSpeed = 20;
[Tooltip("normal countermovement when not crouching.")]
public float counterMovement = 0.175f;
private float threshold = 0.01f;
[Tooltip("the maximum angle the ground can have relative to the players up direction.")]
public float maxSlopeAngle = 35f;
private Vector3 crouchScale = new Vector3(1, 0.5f, 1);
private Vector3 playerScale;
[Tooltip("forward force for when a crouch is started.")]
public float slideForce = 400;
[Tooltip("countermovement when sliding. this doesnt work the same way as normal countermovement.")]
public float slideCounterMovement = 0.2f;
private bool readyToJump = true;
private float jumpCooldown = 0.25f;
[Tooltip("this determines the jump force but is also applied when jumping off of walls, if you decrease it, you may end up being able to walljump and then get back onto the wall leading to infinite height.")]
public float jumpForce = 550f;
float x, y;
bool jumping;
private Vector3 normalVector = Vector3.up;
[Header("Wallrunning")]
private float actualWallRotation;
private float wallRotationVel;
private Vector3 wallNormalVector;
[Tooltip("when wallrunning, an upwards force is constantly applied to negate gravity by about half (at default), increasing this value will lead to more upwards force and decreasing will lead to less upwards force.")]
public float wallRunGravity = 1;
[Tooltip("when a wallrun is started, an upwards force is applied, this describes that force.")]
public float initialForce = 20f;
[Tooltip("float to choose how much force is applied outwards when ending a wallrun. this should always be greater than Jump Force")]
public float escapeForce = 600f;
private float wallRunRotation;
[Tooltip("how much you want to rotate the camera sideways while wallrunning")]
public float wallRunRotateAmount = 10f;
[Tooltip("a bool to check if the player is wallrunning because thats kinda necessary.")]
public bool isWallRunning;
[Tooltip("a bool to determine whether or not to actually allow wallrunning.")]
public bool useWallrunning = true;
[Header("Collisions")]
[Tooltip("a bool to check if the player is on the ground.")]
public bool grounded;
[Tooltip("a bool to check if the player is currently crouching.")]
public bool crouching;
private bool surfing;
private bool cancellingGrounded;
private bool cancellingSurf;
private bool cancellingWall;
private bool onWall;
private bool cancelling;
public static PlayerMovement Instance { get; private set; }
void Awake()
{
Instance = this;
rb = GetComponent<Rigidbody>();
//Create a physic material with no friction to allow for wallrunning and smooth movement not being dependant
//and smooth movement not being dependant on the in-built unity physics engine, apart from collisions.
PhysicMaterial mat = new PhysicMaterial("tempMat");
mat.bounceCombine = PhysicMaterialCombine.Average;
mat.bounciness = 0;
mat.frictionCombine = PhysicMaterialCombine.Minimum;
mat.staticFriction = 0;
mat.dynamicFriction = 0;
gameObject.GetComponent<Collider>().material = mat;
}
void Start()
{
playerScale = transform.localScale;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
readyToJump = true;
wallNormalVector = Vector3.up;
}
private void FixedUpdate()
{
Movement();
}
private void Update()
{
MyInput();
Look();
}
private void LateUpdate()
{
//call the wallrunning Function
WallRunning();
WallRunRotate();
}
private void WallRunRotate()
{
FindWallRunRotation();
float num = 12f;
actualWallRotation = Mathf.SmoothDamp(actualWallRotation, wallRunRotation, ref wallRotationVel, num * Time.deltaTime);
playerCam.localRotation = Quaternion.Euler(playerCam.rotation.eulerAngles.x, playerCam.rotation.eulerAngles.y, actualWallRotation);
}
/// <summary>
/// Find user input. Should put this in its own class but im lazy
/// </summary>
private void MyInput()
{
x = Input.GetAxisRaw("Horizontal");
y = Input.GetAxisRaw("Vertical");
jumping = Input.GetButton("Jump");
crouching = Input.GetKey(KeyCode.LeftControl);
//Crouching
if (Input.GetKeyDown(KeyCode.LeftControl))
StartCrouch();
if (Input.GetKeyUp(KeyCode.LeftControl))
StopCrouch();
}
private void StartCrouch()
{
transform.localScale = crouchScale;
transform.position = new Vector3(transform.position.x, transform.position.y - 0.5f, transform.position.z);
if (rb.velocity.magnitude > 0.2f && grounded)
{
if (grounded)
{
rb.AddForce(orientation.transform.forward * slideForce);
}
}
}
private void StopCrouch()
{
transform.localScale = playerScale;
transform.position = new Vector3(transform.position.x, transform.position.y + 0.5f, transform.position.z);
}
private void Movement()
{
//Extra gravity
rb.AddForce(Vector3.down * Time.deltaTime * 10);
//Find actual velocity relative to where player is looking
Vector2 mag = FindVelRelativeToLook();
float xMag = mag.x, yMag = mag.y;
//Counteract sliding and sloppy movement
CounterMovement(x, y, mag);
//If holding jump && ready to jump, then jump
if (readyToJump && jumping) Jump();
//Set max speed
float maxSpeed = this.maxSpeed;
//If sliding down a ramp, add force down so player stays grounded and also builds speed
if (crouching && grounded && readyToJump)
{
rb.AddForce(Vector3.down * Time.deltaTime * 3000);
return;
}
//If speed is larger than maxspeed, cancel out the input so you don't go over max speed
if (x > 0 && xMag > maxSpeed) x = 0;
if (x < 0 && xMag < -maxSpeed) x = 0;
if (y > 0 && yMag > maxSpeed) y = 0;
if (y < 0 && yMag < -maxSpeed) y = 0;
//Some multipliers
float multiplier = 1f, multiplierV = 1f;
// Movement in air
if (!grounded)
{
multiplier = 0.5f;
multiplierV = 0.5f;
}
// Movement while sliding
if (grounded && crouching) multiplierV = 0f;
//Apply forces to move player
rb.AddForce(orientation.transform.forward * y * moveSpeed * Time.deltaTime * multiplier * multiplierV);
rb.AddForce(orientation.transform.right * x * moveSpeed * Time.deltaTime * multiplier);
}
private void Jump()
{
if ((grounded || isWallRunning || surfing) && readyToJump)
{
MonoBehaviour.print("jumping");
Vector3 velocity = rb.velocity;
readyToJump = false;
rb.AddForce(Vector2.up * jumpForce * 1.5f);
rb.AddForce(normalVector * jumpForce * 0.5f);
if (rb.velocity.y < 0.5f)
{
rb.velocity = new Vector3(velocity.x, 0f, velocity.z);
}
else if (rb.velocity.y > 0f)
{
rb.velocity = new Vector3(velocity.x, velocity.y / 2f, velocity.z);
}
if (isWallRunning)
{
rb.AddForce(wallNormalVector * jumpForce * 3f);
}
Invoke("ResetJump", jumpCooldown);
if (isWallRunning)
{
isWallRunning = false;
}
}
}
private void ResetJump()
{
readyToJump = true;
}
private float desiredX;
private void Look()
{
float mouseX = Input.GetAxis("Mouse X") * sensitivity * Time.fixedDeltaTime * sensMultiplier;
float mouseY = Input.GetAxis("Mouse Y") * sensitivity * Time.fixedDeltaTime * sensMultiplier;
//Find current look rotation
Vector3 rot = playerCam.transform.localRotation.eulerAngles;
desiredX = rot.y + mouseX;
//Rotate, and also make sure we dont over- or under-rotate.
xRotation -= mouseY;
float clamp = 89.5f;
xRotation = Mathf.Clamp(xRotation, -clamp, clamp);
//Perform the rotations
playerCam.transform.localRotation = Quaternion.Euler(xRotation, desiredX, 0);
orientation.transform.localRotation = Quaternion.Euler(0, desiredX, 0);
}
private void CounterMovement(float x, float y, Vector2 mag)
{
if (!grounded || jumping) return;
//Slow down sliding
if (crouching)
{
rb.AddForce(moveSpeed * Time.deltaTime * -rb.velocity.normalized * slideCounterMovement);
return;
}
//Counter movement
if (Math.Abs(mag.x) > threshold && Math.Abs(x) < 0.05f || (mag.x < -threshold && x > 0) || (mag.x > threshold && x < 0))
{
rb.AddForce(moveSpeed * orientation.transform.right * Time.deltaTime * -mag.x * counterMovement);
}
if (Math.Abs(mag.y) > threshold && Math.Abs(y) < 0.05f || (mag.y < -threshold && y > 0) || (mag.y > threshold && y < 0))
{
rb.AddForce(moveSpeed * orientation.transform.forward * Time.deltaTime * -mag.y * counterMovement);
}
//Limit diagonal running. This will also cause a full stop if sliding fast and un-crouching, so not optimal.
if (Mathf.Sqrt((Mathf.Pow(rb.velocity.x, 2) + Mathf.Pow(rb.velocity.z, 2))) > maxSpeed)
{
float fallspeed = rb.velocity.y;
Vector3 n = rb.velocity.normalized * maxSpeed;
rb.velocity = new Vector3(n.x, fallspeed, n.z);
}
}
/// <summary>
/// Find the velocity relative to where the player is looking
/// Useful for vectors calculations regarding movement and limiting movement
/// </summary>
/// <returns></returns>
public Vector2 FindVelRelativeToLook()
{
float lookAngle = orientation.transform.eulerAngles.y;
float moveAngle = Mathf.Atan2(rb.velocity.x, rb.velocity.z) * Mathf.Rad2Deg;
float u = Mathf.DeltaAngle(lookAngle, moveAngle);
float v = 90 - u;
float magnitue = rb.velocity.magnitude;
float yMag = magnitue * Mathf.Cos(u * Mathf.Deg2Rad);
float xMag = magnitue * Mathf.Cos(v * Mathf.Deg2Rad);
return new Vector2(xMag, yMag);
}
//a lot of math (dont touch)
private void FindWallRunRotation()
{
if (!isWallRunning)
{
wallRunRotation = 0f;
return;
}
_ = new Vector3(0f, playerCam.transform.rotation.y, 0f).normalized;
new Vector3(0f, 0f, 1f);
float num = 0f;
float current = playerCam.transform.rotation.eulerAngles.y;
if (Math.Abs(wallNormalVector.x - 1f) < 0.1f)
{
num = 90f;
}
else if (Math.Abs(wallNormalVector.x - -1f) < 0.1f)
{
num = 270f;
}
else if (Math.Abs(wallNormalVector.z - 1f) < 0.1f)
{
num = 0f;
}
else if (Math.Abs(wallNormalVector.z - -1f) < 0.1f)
{
num = 180f;
}
num = Vector3.SignedAngle(new Vector3(0f, 0f, 1f), wallNormalVector, Vector3.up);
float num2 = Mathf.DeltaAngle(current, num);
wallRunRotation = (0f - num2 / 90f) * wallRunRotateAmount;
if (!useWallrunning)
{
return;
}
if ((Mathf.Abs(wallRunRotation) < 4f && y > 0f && Math.Abs(x) < 0.1f) || (Mathf.Abs(wallRunRotation) > 22f && y < 0f && Math.Abs(x) < 0.1f))
{
if (!cancelling)
{
cancelling = true;
CancelInvoke("CancelWallrun");
Invoke("CancelWallrun", 0.2f);
}
}
else
{
cancelling = false;
CancelInvoke("CancelWallrun");
}
}
private bool IsFloor(Vector3 v)
{
return Vector3.Angle(Vector3.up, v) < maxSlopeAngle;
}
private bool IsSurf(Vector3 v)
{
float num = Vector3.Angle(Vector3.up, v);
if (num < 89f)
{
return num > maxSlopeAngle;
}
return false;
}
private bool IsWall(Vector3 v)
{
return Math.Abs(90f - Vector3.Angle(Vector3.up, v)) < 0.05f;
}
private bool IsRoof(Vector3 v)
{
return v.y == -1f;
}
/// <summary>
/// Handle ground detection
/// </summary>
private void OnCollisionStay(Collision other)
{
int layer = other.gameObject.layer;
if ((int)whatIsGround != ((int)whatIsGround | (1 << layer)))
{
return;
}
for (int i = 0; i < other.contactCount; i++)
{
Vector3 normal = other.contacts[i].normal;
if (IsFloor(normal))
{
if (isWallRunning)
{
isWallRunning = false;
}
grounded = true;
normalVector = normal;
cancellingGrounded = false;
CancelInvoke("StopGrounded");
}
if (IsWall(normal) && (layer == (int)whatIsGround || (int)whatIsGround == -1 || layer == LayerMask.NameToLayer("Ground") || layer == LayerMask.NameToLayer("ground"))) //seriously what is this
{
StartWallRun(normal);
onWall = true;
cancellingWall = false;
CancelInvoke("StopWall");
}
if (IsSurf(normal))
{
surfing = true;
cancellingSurf = false;
CancelInvoke("StopSurf");
}
IsRoof(normal);
}
float num = 3f;
if (!cancellingGrounded)
{
cancellingGrounded = true;
Invoke("StopGrounded", Time.deltaTime * num);
}
if (!cancellingWall)
{
cancellingWall = true;
Invoke("StopWall", Time.deltaTime * num);
}
if (!cancellingSurf)
{
cancellingSurf = true;
Invoke("StopSurf", Time.deltaTime * num);
}
}
private void StopGrounded()
{
grounded = false;
}
private void StopWall()
{
onWall = false;
isWallRunning = false;
}
private void StopSurf()
{
surfing = false;
}
//wallrunning functions
private void CancelWallrun()
{
//for when we want to stop wallrunning
MonoBehaviour.print("cancelled wallrun");
Invoke("GetReadyToWallrun", 0.1f);
rb.AddForce(wallNormalVector * escapeForce);
isWallRunning = false;
}
private void StartWallRun(Vector3 normal)
{
MonoBehaviour.print("wallrunning");
//cancels all y momentum and then applies an upwards force.
if (!grounded && useWallrunning)
{
wallNormalVector = normal;
if (!isWallRunning)
{
rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.AddForce(Vector3.up * initialForce, ForceMode.Impulse);
}
isWallRunning = true;
}
}
private void WallRunning()
{
//checks if the wallrunning bool is set to true and if it is then applies
//a force to counter gravity enough to make it feel like wallrunning
if (isWallRunning)
{
rb.AddForce(-wallNormalVector * Time.deltaTime * moveSpeed);
rb.AddForce(Vector3.up * Time.deltaTime * rb.mass * 40f * wallRunGravity * -Physics.gravity.y);
}
}
}
I used a addForce funktion to add the recoil.Please help me.
This sentence is juist written because i need to add more details
hi there thanks for taking the time to look at my question
im currenty making a 2D side scroller/platformer and im currently facing a issue where my enemies are piling up on top of each other. through googling i see alot of Astar pathfinding solving this however i hesitant to jump into ai right now as i glanced over its internal scripts and they are outside my current knowledge and im still attempting to get a understanding of just using scripting to achieve what i want.
i currently have my enemies correctly chasing my player with a vector2.movetowards however eventually they all pile on each other.
through google i have found this is a typical outcome of movetowards and i found this solution
https://answers.unity.com/questions/1608266/stop-enemies-from-grouping-up.html
and i implemented its code
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
}
}
however i needed to add "enemy" to each transform.position or they would instantly snap from my spawn location to the player.
after adding enemy they stopped snapping over a large distance except now they started piling again
all of my debug logs look like they are printing out the correct values
i was also reading through transform.translate as looking at my "sum" values this should be how much they are moving away from each other
so i changed the line
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
to
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
now it seems to only effect one fish and the others clump together still
from my understanding physics.overlapcircle all should be functioning as a list?
heres my entire code albeit long(still relatively new to coding)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DartFish : MonoBehaviour
{
public Transform target;
public float movementSpeed;
private float initialMovementSpeed;
public float dashSpeed;
private float spawnSpeed = 1.5f;
public float chaseRadius;
private float initialChaseRadius;
public float dashRadius;
private Vector2 targetDirection;
private float directionX;
private float previousDirectionX;
private float directionY;
private float spriteAngle;
private float previousAngle;
private float currentSpriteAngle;
private float difference;
private SpriteRenderer mySpriteRenderer;
private Rigidbody2D myRigidbody;
private bool directionIsAboutToChange;
private bool directionHasChanged;
private Vector3 angleCompensation;
// avoid others variables
public float avoidenceSpeed;
public float avoidenceRadius;
public Transform enemy;
private Vector2 sum = Vector2.zero;
private float count;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindWithTag("dartfish").transform;
target = GameObject.FindWithTag("Player").transform;
mySpriteRenderer = GetComponent<SpriteRenderer>();
myRigidbody = GetComponent<Rigidbody2D>();
initialMovementSpeed = movementSpeed;
initialChaseRadius = chaseRadius;
}
// Update is called once per frame
void Update()
{
targetDirection = target.position - transform.position;// where is the player?
directionX = targetDirection.x;//extract only x for "check direction"
directionY = targetDirection.y;
if(directionX < -initialChaseRadius + .5f)// is the player further then the chase radius?
{
myRigidbody.velocity = new Vector2(-1.5f, 0);
}
else
{
myRigidbody.velocity = Vector2.zero;
}
if(directionX > 0)//calculate tangent angle to the player in radians and then convert it to degrees
{
spriteAngle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
}
else if(directionX < 0)//if player is to the left flip opp/adj and add 100° to zero angle
{
spriteAngle = Mathf.Atan2(targetDirection.x, targetDirection.y) * Mathf.Rad2Deg +100f;
}
currentSpriteAngle = spriteAngle;
difference = spriteAngle - previousAngle;
difference = Mathf.Abs(difference);
if(directionX < 1 && directionX > -1)//are we about to change directions?
{
//Debug.Log("direction is about to change");
directionIsAboutToChange = true;
}
if(directionIsAboutToChange == true)//did we pass through trigger zone?
{
if(directionX > 1 || directionX < -1)
//Debug.Log("direction has changed");
directionHasChanged = true;
}
lookAtMe();
chaseMe();
avoidOthers();
/*
//debug logs with spam control
if(previousAngle != spriteAngle)
{
Debug.Log("angle = " + spriteAngle);
}
if(difference != 0)
{
Debug.Log("difference = " + difference);
}
if(previousDirectionX != directionX)
{
Debug.Log("directionX = " + directionX);
}
if(previousAngle != currentSpriteAngle)
{
Debug.Log("previous angle = " + previousAngle);
}
*/
previousAngle = currentSpriteAngle;
previousDirectionX = directionX;
}
void lookAtMe()
{
if(directionHasChanged == true)//angle compensation for sprite flip
{
if(directionY > 0)//is the player above or below us
{
angleCompensation = transform.localEulerAngles;//get current rotation
//flip it
if(directionX > 0)
{
transform.localEulerAngles = -angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = angleCompensation;
}
}
if(directionY < 0)
{
if(directionX > 0)
{
transform.localEulerAngles = angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = -angleCompensation;
}
}
directionHasChanged = false;
directionIsAboutToChange = false;
}
if(directionX < -1)
{
mySpriteRenderer.flipX = true;
//Debug.Log("entered void lookAtMe.directionX <");
if(spriteAngle > previousAngle && spriteAngle < 50f )
{
transform.Rotate(0f, 0f, -difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, difference);
}
}
else if(directionX > 1)
{
mySpriteRenderer.flipX = false;
//Debug.Log("entered void lookAtMe.directionX >");
if(spriteAngle > previousAngle && spriteAngle < 50f)
{
transform.Rotate(0f, 0f, difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, -difference);
}
}
}
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
}
}
void chaseMe()
{
if(Vector3.Distance(target.position, transform.position) <= chaseRadius && Vector3.Distance(target.position, transform.position) > dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, movementSpeed * Time.deltaTime);
}
else if(Vector3.Distance(target.position, transform.position) <= dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, dashSpeed * Time.deltaTime);
}
}
}
please help me stop my enemys from clumping up without the use of AI
thanks again for any assistance and your time its much appreciated
I make 3rd person game, where i have script for control model of my character.
Problem is that when i jump and don't push any button he stack in the air, but if i jump with pushing controlling button he worked normally.
Thanks in advance.
Player controller script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CharacterController))]
public class RelativeMovement : MonoBehaviour
{
[SerializeField] private Transform target;
public float rotSpeed = 5.0f;
public float moveSpeed = 6.0f;
public float jumpSpeed = 15.0f;
public float gravity = -9.8f;
public float terminalVelocity = -20.0f;
public float minFall = -1.5f;
private float _vertSpeed;
private CharacterController _charController;
private ControllerColliderHit _contact;
void Start()
{
_vertSpeed = minFall;
_charController = GetComponent<CharacterController>();
}
void Update()
{
Vector3 movement = Vector3.zero;
float horInput = Input.GetAxis("Horizontal");
float vertInput = Input.GetAxis("Vertical");
if(horInput != 0 || vertInput != 0)
{
movement.x = horInput * moveSpeed;
movement.z = vertInput * moveSpeed;
movement = Vector3.ClampMagnitude(movement, moveSpeed);
Quaternion tmp = target.rotation;
target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);
movement = target.TransformDirection(movement);
target.rotation = tmp;
Quaternion direction = Quaternion.LookRotation(movement);
transform.rotation = Quaternion.Lerp(transform.rotation,
direction, rotSpeed * Time.deltaTime);
bool hitGround = false;
RaycastHit hit;
if(_vertSpeed < 0 && Physics.Raycast(transform.position, Vector3.down, out hit))
{
float check =
(_charController.height + _charController.radius) / 1.9f;
hitGround = hit.distance <= check;
}
if(hitGround)
{
if (Input.GetButtonDown("Jump"))
{
_vertSpeed = jumpSpeed;
}
else
{
_vertSpeed = minFall;
}
}
else
{
_vertSpeed += gravity * 5 * Time.deltaTime;
if(_vertSpeed < terminalVelocity)
{
_vertSpeed = terminalVelocity;
}
if(_charController.isGrounded)
{
if(Vector3.Dot(movement, _contact.normal) < 0)
{
movement = _contact.normal * moveSpeed;
}
else
{
movement += _contact.normal * moveSpeed;
}
}
}
movement.y = _vertSpeed;
movement *= Time.deltaTime;
_charController.Move(movement);
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
_contact = hit;
}
}
Jesus photo:
enter image description here
I didn't go through all of the code but I can immediately see what can cause the problem you describe:
your entire logic happen under this if statement:
if(horInput != 0 || vertInput != 0)
So everything inside this logic block will happen only when you actively press a movement button.
You can remove it (or better yet, close the if block at the right spot.. I think it would be 2 lines down)
The answer of this question is:
if((horInput == 0 || vertInput == 0) || (horInput != 0 && vertInput != 0))
{
movement.x = horInput * moveSpeed;
movement.z = vertInput * moveSpeed;
if(horInput != 0 || vertInput != 0)
{
movement = Vector3.ClampMagnitude(movement, moveSpeed);
Quaternion tmp = target.rotation;
target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0);
movement = target.TransformDirection(movement);
target.rotation = tmp;
Quaternion direction = Quaternion.LookRotation(movement);
transform.rotation = Quaternion.Lerp(transform.rotation,
direction, rotSpeed * Time.deltaTime);
}
Thanks Joe
I am working on a Pong-Clone for Android right now and have watched a tutorial about it. It works perfectly with the keyboard, but how do I get a similar control on the mobile phone. My current code looks like this:
private void Start()
{
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Ball>();
col = GetComponent<BoxCollider2D>();
if (side == Side.Left)
forwardDirection = Vector2.right;
else if (side == Side.Right)
forwardDirection = Vector2.left;
}
private void Update()
{
if (!overridePosition)
MovePaddle();
}
private void MovePaddle()
{
float targetYPosition = GetNewYPosition();
ClampPosition(ref targetYPosition);
transform.position = new Vector3(transform.position.x, targetYPosition, transform.position.z);
}
private void ClampPosition(ref float yPosition)
{
float minY = Camera.main.ScreenToWorldPoint(new Vector3(0, 0)).y;
float maxY = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height)).y;
yPosition = Mathf.Clamp(yPosition, minY, maxY);
}
private float GetNewYPosition()
{
float result = transform.position.y;
if (isAI)
{
if (BallIncoming())
{
if (firstIncoming)
{
firstIncoming = false;
randomYOffset = GetRandomOffset();
}
result = Mathf.MoveTowards(transform.position.y, ball.transform.position.y + randomYOffset, moveSpeed * Time.deltaTime);
}
else
{
firstIncoming = true;
}
}
else
{
float movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
result = transform.position.y + movement;
}
return result;
}
I want the player to move on the y Axis, but I have no idea how I can do it with touch controls (I am new to games). I would really appreciate any help, so I can continue programming :)
I'm going to assume what you want isove the padel by touch and drag up or down.
I would keep your code for debugging and add a switch to only use touch if touch is supported and your code otherwise.
// Adjust via the Inspector how much the user has to drag in order to move the padel
[SerializeField] private float touchDragSensitivity = 1;
private float lastTouchPosY;
// Make sure to store the camera
// Using Camera.main is quite expensive!
[SerializeField] private Camera _mainCamera;
private void Awake()
{
if(!_mainCamera) _mainCamera = GetComponent<Camera>();
...
}
private float GetNewPosition()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = _mainCamera.ScreenToWorldPoint(touch.position).y;
if(touch.phase = TouchPhase.Moved)
{
var delta = touchPositionY - lastTouchPosY;
movement = delta * touchDragSensitivity;
}
lastTouchPosY = touchPositionY;
}
}
result = transform.position.y + movement;
}
}
Or alternatively if you rather wanted a similar experience as for the button input you could also only check whether the touch is on the bottom or top half of the screen and move the padel constantly like with your original code before:
private void Update()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = touch.position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
result = transform.position.y + movement;
}
}
Update
As to your question
is there some way to split the screen so two players can play on the same screen
Yes: You can add a check on which side a touch is using touch.position.x >= screen.width / 2f => right side.
You could e.g. filter for valid touches like
if(Input.touchCount > 0)
{
if(side == Side.Right)
{
var validTouches = Input.touches.Where(t => t.position >= Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
else
{
var validTouches = Input.touches.Where(t => t.position < Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
}
If you want to press and hold make the object move you can make two buttons one for +y and the other for -y. then use Input.GetTouch to register the players input.
I am currently in the process of developing my first platformer, and I've decided that I want to bake my own acceleration and deceleration for movement instead of using unity's wonky physics.
This has brought up two roadblocks, one being that my character will just clip through objects like walls to jump over or off of, and the other is that if I try to switch directions while decelerating, my character will turn around, but it won't change directions and my acceleration will bypass the limit I set for it (in layman's, it zooms backwards).
I need help fixing this and can describe any of the functions of the variables if asked. There is no physics material on my character, and I am using physics for the jump.
All of the variables are defined in the unity editor for easier tweaking except for currentSpeed, jumpCount, FallTime and canJump, which I have serialized so I can observe them during testing.
I've tried changing
newPos = new Vector2(transform.position.x + currentSpeed, transform.position.y);
transform.position = newPos;
to addForce but it messed with the jumping.
public class PlayerControls : MonoBehaviour {
//walk Vars
[SerializeField] float currentSpeed;
[SerializeField] float maxSpeed;
[SerializeField] float jumpstart;
[SerializeField] float acceleration;
[SerializeField] float deceleraton;
int isFlip = 1;
Vector2 newPos;
Quaternion flip;
//jump Vars
[SerializeField] public int jumpCount;
[SerializeField] public int jumpCountMax;
[SerializeField] public float jumpVel;
[SerializeField] public float fallMulti;
[SerializeField] public float shortMulti;
[SerializeField] public float fallTime;
[SerializeField] public float coyoteTime;
[SerializeField] bool canJump = true;
[SerializeField] bool didJump;
//references
Rigidbody2D rb;
private void Awake() {
rb = GetComponent<Rigidbody2D>();
}
//walk Functions
private void speedCalc() {
currentSpeed += jumpstart * isFlip;
if (currentSpeed * isFlip >= maxSpeed) {
currentSpeed = maxSpeed * isFlip;
}
else {
currentSpeed = (currentSpeed * acceleration);
}
}
private void Start() {
currentSpeed = 0;
}
private void Update() {
//calculate coyote time
if (fallTime == coyoteTime) {
if (didJump == false) {
jumpCount += 1;
}
}
if (jumpCount >= jumpCountMax) {
canJump = false;
} else {
canJump = true;
}
if (rb.velocity.y == 0) {
jumpCount = 0;
didJump = false;
}
//calculate isFlip
if (Input.GetButton("Right")) {
flip = Quaternion.Euler(0, 0, 0);
transform.rotation = flip;
isFlip = 1;
} else if (Input.GetButton("Left")) {
flip = Quaternion.Euler(0, 180, 0);
transform.rotation = flip;
isFlip = -1;
}
//calculate acceleration & deceleration
if (!Input.GetButton("Right") && !Input.GetButton("Left")) {
if (currentSpeed * isFlip <= jumpstart) {
currentSpeed = 0;
} else {
currentSpeed = (currentSpeed / deceleraton);
}
} else {
speedCalc();
}
//implement falling and dyanamic jumping
if (rb.velocity.y < 0) {
fallTime += 1;
rb.velocity += Vector2.up * Physics2D.gravity * (fallMulti - 1) * Time.deltaTime;
} else if (rb.velocity.y == 0) {
fallTime = 0;
} else if (rb.velocity.y > 0 && !Input.GetButton("Jump")) {
rb.velocity += Vector2.up * Physics2D.gravity * (shortMulti - 1) * Time.deltaTime;
}
//execute jump
if (Input.GetButtonDown("Jump") && canJump == true) {
didJump = true;
rb.velocity = Vector2.up * jumpVel;
jumpCount += 1;
}
//move character position
newPos = new Vector2(transform.position.x + currentSpeed, transform.position.y);
transform.position = newPos;
}
}
There where no error messages or anything but I'm 80% sure its my logic and ordering. The acceleration might be overlapping with the deceleration and the isFlip.