So I am trying to create a game where a player can swing with a rope/grappling hook, similar to a game like Teeworlds. The grappling hook feels great when I am hooked onto things and I am using my movement keys, the problem comes when I let go of my left or right movement keys the character slowly floats down. Feels like the gravity gets put to like 0.00001 on the character until i let go of the grappling hook button.
Will post a gif of how it looks and the code I am using for the grappling hook.
Thanks,
The Pepsidi.
Game Play of Issue
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class GrappleSystem : MonoBehaviour
{
public GameObject grappleAnchor;
public DistanceJoint2D grappleJoint;
public Transform crosshair;
public PlayerMovement playerMovement;
private bool grappleAttached;
private Vector2 playerPosition;
private Rigidbody2D grappleAnchorRb;
private SpriteRenderer grappleAnchorSprite;
public LineRenderer grappleRenderer;
public LayerMask grappleLayerMask;
private float grappleMaxCastDistance = 20f;
private List<Vector2> grapplePositions = new List<Vector2>();
private bool distanceSet;
void Awake()
{
grappleJoint.enabled = false;
playerPosition = transform.position;
grappleAnchorRb = grappleAnchor.GetComponent<Rigidbody2D>();
grappleAnchorSprite = grappleAnchor.GetComponent<SpriteRenderer>();
}
void Update()
{
var worldMousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0f));
var facingDirection = worldMousePosition - transform.position;
var aimAngle = Mathf.Atan2(facingDirection.y, facingDirection.x);
if (aimAngle < 0f)
{
aimAngle = Mathf.PI * 2 + aimAngle;
}
var aimDirection = Quaternion.Euler(0, 0, aimAngle * Mathf.Rad2Deg) * Vector2.right;
playerPosition = transform.position;
if (!grappleAttached)
{
SetCrosshairPosition(aimAngle);
}
HandleInput(aimDirection);
UpdateGrapplePositions();
}
private void SetCrosshairPosition(float aimAngle)
{
var x = transform.position.x + 5f * Mathf.Cos(aimAngle);
var y = transform.position.y + 5f * Mathf.Sin(aimAngle);
var crossHairPosition = new Vector3(x, y, 0);
crosshair.transform.position = crossHairPosition;
}
private void HandleInput(Vector2 aimDirection)
{
if (Input.GetMouseButtonDown(1))
{
if (grappleAttached) return;
grappleRenderer.enabled = true;
var hit = Physics2D.Raycast(playerPosition, aimDirection, grappleMaxCastDistance, grappleLayerMask);
if (hit.collider != null)
{
grappleAttached = true;
if (!grapplePositions.Contains(hit.point))
{
transform.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 5f), ForceMode2D.Force);
grapplePositions.Add(hit.point);
grappleJoint.distance = Vector2.Distance(playerPosition, hit.point);
grappleJoint.enabled = true;
grappleAnchorSprite.enabled = true;
}
}
else
{
grappleRenderer.enabled = false;
grappleAttached = false;
grappleJoint.enabled = false;
}
}
if (Input.GetMouseButtonUp(1))
{
ResetGrapple();
}
}
private void ResetGrapple()
{
grappleJoint.enabled = false;
grappleAttached = false;
//playerMovement.isSwinging = false;
grappleRenderer.positionCount = 2;
grappleRenderer.SetPosition(0, transform.position);
grappleRenderer.SetPosition(1, transform.position);
grapplePositions.Clear();
grappleAnchorSprite.enabled = false;
}
private void UpdateGrapplePositions()
{
if (!grappleAttached)
{
return;
}
grappleRenderer.positionCount = grapplePositions.Count + 1;
for (var i = grappleRenderer.positionCount - 1; i >= 0; i--)
{
if (i != grappleRenderer.positionCount - 1)
{
grappleRenderer.SetPosition(i, grapplePositions[i]);
if (i == grapplePositions.Count - 1 || grapplePositions.Count == 1)
{
var grapplePosition = grapplePositions[grapplePositions.Count - 1];
if (grapplePositions.Count == 1)
{
grappleAnchorRb.transform.position = grapplePosition;
if (!distanceSet)
{
grappleJoint.distance = Vector2.Distance(transform.position, grapplePosition);
distanceSet = true;
}
}
else
{
grappleAnchorRb.transform.position = grapplePosition;
if (!distanceSet)
{
grappleJoint.distance = Vector2.Distance(transform.position, grapplePosition);
distanceSet = true;
}
}
}
else if (i - 1 == grapplePositions.IndexOf(grapplePositions.Last()))
{
var grapplePosition = grapplePositions.Last();
grappleAnchorRb.transform.position = grapplePosition;
if (!distanceSet)
{
grappleJoint.distance = Vector2.Distance(transform.position, grapplePosition);
distanceSet = true;
}
}
}
else
{
grappleRenderer.SetPosition(i, transform.position);
}
}
}
}
Related
For example the duration is set to 10 seconds. When the flag bool go is true the transform will move in 10 seconds to the target(destinationTransform). but when I press the b key in the middle while the transform is moving forward the transform start moving backward to the original position but not in 10 seconds but faster.
Is that because the distance is not the same as when it's moving forward ?
I'm trying to do that when i press the b key in the middle the transform will move backward but in the duration time no matter the distance. maybe i messed the lerp code in the backward state ?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Cinemachine;
public class MoveToTarget : MonoBehaviour
{
public enum TransitionState
{
None,
MovingTowards,
MovingBackward,
Transferring
}
public Transform destinationTransform;
public bool isChild = false;
public AnimationCurve curve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
public float duration = 10.0f;
public bool go = false;
private float t;
private Transform originTransform;
private float timer;
private TransitionState state = TransitionState.MovingTowards;
private Vector3 originPosition;
private SphereCollider col;
private bool enableCollider = true;
private bool updateOriginPosition = true;
void Start()
{
t = 0.0f;
curve.postWrapMode = WrapMode.Once;
originPosition = transform.position;
col = GetComponent<SphereCollider>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.B))
{
t = 0.0f;
state = TransitionState.MovingBackward;
}
if (go)
{
if (updateOriginPosition == true)
{
originPosition = transform.position;
updateOriginPosition = false;
}
if (col != null && enableCollider)
{
//col.enabled = false;
//transform.GetComponent<Rigidbody>().isKinematic = true;
Destroy(col);
Destroy(transform.GetComponent<Rigidbody>());
//transform.GetComponent<InteractableItem>().enabledInteraction = false;
enableCollider = false;
}
switch (state)
{
case TransitionState.MovingTowards:
var v = destinationTransform.position - transform.position;
if (v.magnitude < 0.001f)
{
state = TransitionState.Transferring;
originTransform = destinationTransform;
timer = 0;
return;
}
t += Time.deltaTime;
float s = t / duration;
transform.position = Vector3.Lerp(originPosition,
destinationTransform.position, curve.Evaluate(s));
break;
case TransitionState.MovingBackward:
t += Time.deltaTime;
float s1 = t / duration;
transform.position = Vector3.Lerp(transform.position,
transform.parent.position,
curve.Evaluate(s1));
break;
case TransitionState.Transferring:
timer += Time.deltaTime;
this.transform.position = Vector3.Lerp(originTransform.position, destinationTransform.position, timer);
if (timer >= 1.0f)
{
this.transform.parent = destinationTransform;
transform.localPosition = new Vector3(0, 0, 0);
isChild = true;
go = false;
state = TransitionState.None;
this.enabled = false;
return;
}
break;
default:
this.enabled = false;
return;
}
}
}
}
The solution is first to update the originPosition in the Update when pressing the B key :
if(Input.GetKeyDown(KeyCode.B))
{
t = 0.0f;
originPosition = transform.position;
state = TransitionState.MovingBackward;
}
Then in the MovingBackward state to set the originPosition instead the transform.position.
case TransitionState.MovingBackward:
t += Time.deltaTime;
float s1 = t / duration;
transform.position = Vector3.Lerp(originPosition,
transform.parent.position,
curve.Evaluate(s1));
break;
The complete code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Cinemachine;
public class MoveToTarget : MonoBehaviour
{
public enum TransitionState
{
None,
MovingTowards,
MovingBackward,
Transferring
}
public Transform destinationTransform;
public bool isChild = false;
public AnimationCurve curve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
public float duration = 10.0f;
public bool go = false;
private float t;
private Transform originTransform;
private float timer;
private TransitionState state = TransitionState.MovingTowards;
private Vector3 originPosition;
private SphereCollider col;
private bool enableCollider = true;
private bool updateOriginPosition = true;
void Start()
{
t = 0.0f;
curve.postWrapMode = WrapMode.Once;
originPosition = transform.position;
col = GetComponent<SphereCollider>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.B))
{
t = 0.0f;
originPosition = transform.position;
state = TransitionState.MovingBackward;
}
if (go)
{
if (updateOriginPosition == true)
{
originPosition = transform.position;
updateOriginPosition = false;
}
if (col != null && enableCollider)
{
Destroy(col);
Destroy(transform.GetComponent<Rigidbody>());
enableCollider = false;
}
switch (state)
{
case TransitionState.MovingTowards:
var v = destinationTransform.position - transform.position;
if (v.magnitude < 0.001f)
{
state = TransitionState.Transferring;
originTransform = destinationTransform;
timer = 0;
return;
}
t += Time.deltaTime;
float s = t / duration;
transform.position = Vector3.Lerp(originPosition,
destinationTransform.position, curve.Evaluate(s));
break;
case TransitionState.MovingBackward:
t += Time.deltaTime;
float s1 = t / duration;
transform.position = Vector3.Lerp(originPosition,
transform.parent.position,
curve.Evaluate(s1));
break;
case TransitionState.Transferring:
timer += Time.deltaTime;
this.transform.position = Vector3.Lerp(originTransform.position, destinationTransform.position, timer);
if (timer >= 1.0f)
{
this.transform.parent = destinationTransform;
transform.localPosition = new Vector3(0, 0, 0);
isChild = true;
go = false;
state = TransitionState.None;
this.enabled = false;
return;
}
break;
default:
this.enabled = false;
return;
}
}
}
}
So, I started following a tutorial and then added some other things that I needed and everything works fine, even the recoil, but the problem is that it is really choppy, it moves once a frame and it isn't smooth at all (which is what I want) I don't know a lot about programming so I hope you can help me :)
My code:
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Audio;
public class GunController : MonoBehaviour
{
[Header("Gun Setting")]
public float fireRate = 0.1f;
public int clipSize = 30;
public int reservedAmmoCapacity = 270;
//Variables that change throughout the code
bool canShoot;
int _currentAmmoInClip;
int _ammoInReserve;
//Muzzle Flash
public ParticleSystem muzzleFlash;
//Aiming
public Vector3 normalLocalPosition;
public Vector3 aimingLocalPosition;
public float aimSmoothing = 10;
[Header("Mouse Settings")]
public float mouseSensitivity = 1;
Vector2 _currentRotation;
public float weaponSwayAmount = 10;
//Weapon Recoil
public bool randomizeRecoil;
public Vector2 randomRecoilConstraints;
//You only need to assign if randomize recoil is off
public Vector2[] recoilPattern;
//Audio
AudioSource shootingSound;
//Reloading
public float reloadTime = 1.5f;
private void Start()
{
_currentAmmoInClip = clipSize;
_ammoInReserve = reservedAmmoCapacity;
canShoot = true;
shootingSound = GetComponent<AudioSource>();
}
private void Update()
{
DetermineAim();
DetermineRotation();
if (Input.GetMouseButton(0) && canShoot && _currentAmmoInClip > 0)
{
shootingSound.Play();
StartCoroutine(FinishShooting());
muzzleFlash.Play();
canShoot = false;
_currentAmmoInClip--;
StartCoroutine(ShootGun());
}
else if (Input.GetKeyDown(KeyCode.R) && _currentAmmoInClip < clipSize && _ammoInReserve > 0)
{
StartCoroutine(Reload());
}
}
void DetermineRotation()
{
Vector2 mouseAxis = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
mouseAxis *= mouseSensitivity;
_currentRotation += mouseAxis;
_currentRotation.y = Mathf.Clamp(_currentRotation.y, -90, 90);
transform.localPosition += (Vector3)mouseAxis * weaponSwayAmount / 1000;
transform.root.localRotation = Quaternion.AngleAxis(_currentRotation.x, Vector3.up);
transform.parent.localRotation = Quaternion.AngleAxis(-_currentRotation.y, Vector3.right);
}
void DetermineAim()
{
Vector3 target = normalLocalPosition;
if (Input.GetMouseButton(1)) target = aimingLocalPosition;
Vector3 desiredPosition = Vector3.Lerp(transform.localPosition, target, Time.deltaTime * aimSmoothing);
transform.localPosition = desiredPosition;
}
void DetermineRecoil()
{
transform.localPosition -= Vector3.forward * 0.1f;
if (randomizeRecoil)
{
float xRecoil = Random.Range(-randomRecoilConstraints.x, randomRecoilConstraints.x);
float yRecoil = Random.Range(-randomRecoilConstraints.y, randomRecoilConstraints.y);
Vector2 recoil = new Vector2(xRecoil, yRecoil);
_currentRotation += recoil;
}
else
{
int currentStep = clipSize + 1 - _currentAmmoInClip;
currentStep = Mathf.Clamp(currentStep, 0, recoilPattern.Length - 1);
_currentRotation += recoilPattern[currentStep];
}
}
IEnumerator ShootGun()
{
_currentAmmoInClip -= 1;
DetermineRecoil();
RayCastEnemy();
yield return new WaitForSeconds(fireRate);
canShoot = true;
}
void RayCastEnemy()
{
RaycastHit hit;
if (Physics.Raycast(transform.parent.position, transform.parent.forward, out hit, 1 << LayerMask.NameToLayer("Enemy")))
{
try
{
Debug.Log("Hit an enemy");
Rigidbody rb = hit.transform.GetComponent<Rigidbody>();
rb.constraints = RigidbodyConstraints.None;
rb.AddForce(transform.parent.transform.forward * 50);
}
catch { }
}
}
IEnumerator FinishShooting()
{
yield return new WaitForSeconds(0.3f);
shootingSound.Stop();
}
IEnumerator Reload()
{
canShoot = false;
yield return new WaitForSeconds(reloadTime);
int amountNeeded = clipSize - _currentAmmoInClip;
if (amountNeeded > _ammoInReserve)
{
_currentAmmoInClip += _ammoInReserve;
_ammoInReserve -= amountNeeded;
canShoot = true;
}
else
{
canShoot = false;
_currentAmmoInClip = clipSize;
_ammoInReserve -= amountNeeded;
canShoot = true;
}
}
}
I don't see the function DetermineRecoil() called anywhere in the code, but maybe this solution will work:
let _currentRotation = Vector2.Lerp(_currentRotation, _currentRotation + recoilPattern[currentStep], 10*Time.deltaTime);
It will change the value of _currentRotation smoothly.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCameraBehind : MonoBehaviour
{
public GameObject camera;
public List<GameObject> targets = new List<GameObject>();
public float cameraDistance = 10.0f;
public bool behindMultipleTargets = false;
public string cameraWarningMsgs = "";
public string targetsWarningMsgs = "";
// Use this for initialization
void Start()
{
if (camera == null)
{
var cam = GetComponent<Camera>();
if (cam != null)
{
cameraWarningMsgs = "Gettig camera component.";
camera = transform.gameObject;
}
else
{
cameraWarningMsgs = "Creating a new camera component.";
GameObject NewCam = Instantiate(new GameObject(), transform);
NewCam.name = "New Camera";
NewCam.AddComponent<Camera>();
camera = NewCam;
}
}
if(targets.Count == 0)
{
targetsWarningMsgs = "No targets found.";
}
}
void FixedUpdate()
{
if (targets.Count > 0)
{
MoveCameraToPosition();
}
}
public void MoveCameraToPosition()
{
if (targets.Count > 1 && behindMultipleTargets == true)
{
var center = CalculateCenter();
transform.position = new Vector3(center.x, center.y + 2, center.z + cameraDistance);
}
if (behindMultipleTargets == false)
{
Vector3 center = targets[0].transform.position - targets[0].transform.forward * cameraDistance;
transform.position = new Vector3(center.x, center.y + 2, center.z);
}
}
private Vector3 CalculateCenter()
{
Vector3 center = new Vector3();
var totalX = 0f;
var totalY = 0f;
foreach (var target in targets)
{
totalX += target.transform.position.x;
totalY += target.transform.position.y;
}
var centerX = totalX / targets.Count;
var centerY = totalY / targets.Count;
center = new Vector3(centerX, centerY);
return center;
}
}
The CalculateCenter function make the targets(objects) to change positions and vanish away far away. Even if there is only one single target.
What I want to do is if there is one object for example one 3d cube position the camera behind the cube. And if there are more cubes for example two or ten and the camera is somewhere else calculate the middle position behind the targets and position the camera in the middle behind them.
To show what I mean in this example the view(like a camera) is behind the two soldiers in the middle position between them from behind.
But what if there are 5 soldiers how can I find the middle position and then position the camera behind them like this example in the screenshot ?
This is my old script version was working fine but only for 1 or 2 targets. But if there are 5 targets(soldiers) how can I position the camera behind them in the middle ? Like in the screenshot example.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCameraBehind : MonoBehaviour
{
public GameObject camera;
public List<GameObject> targets = new List<GameObject>();
public float cameraDistance = 10.0f;
public bool behindTwoTargets = false;
public string warningMsgs = "";
// Use this for initialization
void Start()
{
if (camera == null)
{
var cam = GetComponent<Camera>();
if (cam != null)
{
warningMsgs = "Gettig Camera omponent.";
camera = transform.gameObject;
}
else
{
warningMsgs = "Creating a new camera component.";
GameObject NewCam = Instantiate(new GameObject(), transform);
NewCam.name = "New Camera";
NewCam.AddComponent<Camera>();
camera = NewCam;
}
}
camera.transform.Rotate(0, 180, 0);
}
void FixedUpdate()
{
if (targets.Count > 0)
{
MoveCameraToPosition();
}
}
public void MoveCameraToPosition()
{
if (targets.Count == 2 && behindTwoTargets == true)
{
Vector3 center = ((targets[0].transform.position - targets[1].transform.position) / 2.0f) + targets[1].transform.position;
camera.transform.position = new Vector3(center.x, center.y + 2, center.z + cameraDistance);
}
if (behindTwoTargets == false)
{
Vector3 center = targets[0].transform.position - targets[0].transform.forward * cameraDistance;
camera.transform.position = new Vector3(center.x, center.y + 2, center.z);
}
}
}
This is my last version of my working code still using the CalculateCenter function :
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraLook : MonoBehaviour
{
public GameObject camera;
public List<GameObject> targets = new List<GameObject>();
public float cameraDistance = 10.0f;
public float cameraHeight = 2f;
public float rotateTime = 2f;
public bool multipleTargets = false;
public bool changeRandomTarget = false;
public bool behindFront = false;
public bool targetsRandomRot = false;
public string cameraWarningMsgs = "";
public string targetsWarningMsgs = "";
private List<Vector3> vectors = new List<Vector3>();
//Random move rotation timer part
Quaternion qTo;
float speed = 3f;
float timer = 0.0f;
// Use this for initialization
void Start()
{
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(-180.0f, 180.0f), 0.0f));
if (camera == null)
{
var cam = GetComponent<Camera>();
if (cam != null)
{
cameraWarningMsgs = "Gettig camera component.";
camera = transform.gameObject;
}
else
{
cameraWarningMsgs = "Creating a new camera component.";
GameObject NewCam = Instantiate(new GameObject(), transform);
NewCam.name = "New Camera";
NewCam.AddComponent<Camera>();
camera = NewCam;
}
}
if (targets.Count == 0)
{
targetsWarningMsgs = "No targets found.";
}
else
{
foreach(GameObject vector in targets)
{
vectors.Add(vector.transform.position);
}
}
}
void FixedUpdate()
{
if (targets.Count > 0)
{
MoveCameraToPosition();
if (targetsRandomRot == true)
{
RotateTargetsRandom();
}
}
}
public void MoveCameraToPosition()
{
Vector3 center = CalculateCenter();
camera.transform.position = center;
if (behindFront == false)
{
camera.transform.rotation = Quaternion.LookRotation(-center, Vector3.up);
}
else
{
camera.transform.rotation = Quaternion.LookRotation(center, Vector3.up);
}
}
private Vector3 CalculateCenter()
{
Vector3 center = new Vector3();
var x = targets[0].transform.position.x;
var y = targets[0].transform.position.y;
var z = targets[0].transform.position.z;
if (multipleTargets == true)
{
for (int i = 1; i < targets.Count; i++)
{
x += targets[i].transform.position.x;
y += targets[i].transform.position.y;
z += targets[i].transform.position.z;
}
}
else
{
x += targets[0].transform.position.x;
y += targets[0].transform.position.y;
z += targets[0].transform.position.z;
}
if(changeRandomTarget == true)
{
for (int i = 1; i < targets.Count; i++)
{
x += targets[i].transform.position.x;
y += targets[i].transform.position.y;
z += targets[i].transform.position.z;
}
}
x = x / targets.Count;
y = y / targets.Count;
z = z / targets.Count;
if (behindFront == false)
{
center = new Vector3(x, y + cameraHeight, z + cameraDistance);
}
else
{
center = new Vector3(x, y + cameraHeight, z - cameraDistance);
}
return center;
}
private void RotateTargetsRandom()
{
timer += Time.deltaTime;
if (timer > rotateTime)
{ // timer resets at 2, allowing .5 s to do the rotating
qTo = Quaternion.Euler(new Vector3(0.0f, Random.Range(-180.0f, 180.0f), 0.0f));
timer = 0.0f;
}
foreach (var target in targets)
{
target.transform.rotation = Quaternion.Slerp(target.transform.rotation, qTo, Time.deltaTime * speed);
}
}
}
And now I want to add and use the bool flag changeRandomTarget :
But not sure how to do it :
if(changeRandomTarget == true)
{
for (int i = 1; i < targets.Count; i++)
{
x += targets[i].transform.position.x;
y += targets[i].transform.position.y;
z += targets[i].transform.position.z;
}
}
I want that each X seconds it will pick a random center and change the camera position according to it. For example the first item in the targets List the last item and all the items so each X seconds the center will be once behind targets[0] or targets1 or targets[i]
Not sure how to do it with my code or with derHugo solution.
Surely you just find the average
so
if (mobcount > 1)
{
var x=mob[0].position.x;
var y=mob[0].position.y;
var z=mob[0].position.z;
for(int i=1; i<mobcount; i++)
{
x += mob[i].position.x;
y += mob[i].position.y;
z += mob[i].position.z;
}
x = x / mobcount;
y = y / mobcount;
z = z / mobcount;
}
therefore the camera should look at the position x,y,z.. and perhaps set the distance to be a fixed distance behind the nearest mob...
Actually you don't even need to do it component wise:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
...
private static Vector3 Average(IReadOnlyCollection<Vector3> vectors)
{
if(vectors == null) return Vector3.zero;
switch (vectors.Count)
{
case 0:
return Vector3.zero;
case 1:
return vectors.First();
default:
var average = Vector3.zero;
foreach(var vector in vectors)
{
average += vector;
}
return average / vectors.Count;
}
}
Or directly using Linq aggregate
private static Vector3 Average(IReadOnlyCollection<Vector3> vectors)
{
if(vectors == null) return Vector3.zero;
switch (vectors.Count)
{
case 0:
return Vector3.zero;
case 1:
return vectors.First();
default:
var average = vectors.Aggregate(Vector3.zero, (current, vector) => current + vector);
return average / vectors.Count;
}
}
I have a homing missile script that seems to work 99% of the time, but every so often one of the missiles will start spiralling out of control and not towards the player. I can't see any reason for it, I can't see a reason for the behaviour in the code and I'm after a pair of fresh eye's to take a look and see if I'm missing something obvious. The script below is attached to each missile.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class missileControl : MonoBehaviour
{
public Transform missileTarget;
public GameObject indicator;
private int scorePerHit = 1;
private int scorePerDestroy = 50;
private int hits = 1;
[Header("Enemy Culling Variables")]
[HideInInspector]
public float distanceToCull = 4f;
[HideInInspector]
private Transform spaceShip;
public Rigidbody missileRigidbody;
[Header("Missile Control Variables")]
public float turn = 0.9f;
private float initialTurn = 0.9f;
public float missileVelocity = 95f;
private float initialMissileVelocity = 95f;
private Vector3 resetMissileVelocity = new Vector3(0f,0f,0f);
private Transform myTransform;
private float wiggleMultiplyer = 40f;
[Header("Firing Audio")]
public float minPitch = 0.9f;
public float maxPitch = 1f;
public AudioClip indicatorClip;
public TrailRenderer trailRenderer;
private bool indicatorOff = true;
private float currentDistance = 100f;
public float indicatorDist = 300f;
private IEnumerator delayAimTimer;
private void Start()
{
spaceShip = LevelVariables.currentLevelVariables.spaceShip.transform; // Player to test if behind
missileTarget = LevelVariables.currentLevelVariables.playerTarget.transform; // Target
missileRigidbody.velocity = resetMissileVelocity;
ResetVariables();
}
private void OnEnable()
{
ResetVariables();
}
private void OnDisable()
{
missileRigidbody.velocity = resetMissileVelocity;
}
void ResetVariables()
{
trailRenderer.enabled = false;
StartCoroutine(DelayTrails());
indicatorOff = true;
turn = Random.Range(initialTurn * 0.85f, initialTurn * 1.15f);
wiggleMultiplyer = Random.Range(0 - (wiggleMultiplyer), wiggleMultiplyer);
missileRigidbody.velocity = resetMissileVelocity;
missileVelocity = Random.Range(initialMissileVelocity * 0.85f, initialMissileVelocity * 1.15f);
missileRigidbody.velocity = transform.forward * missileVelocity;
}
IEnumerator DelayTrails()
{
yield return new WaitForSeconds(0.15f);
trailRenderer.Clear();
trailRenderer.enabled = true;
}
private void FixedUpdate()
{
if (GameStateController.gameStateController.gameState == GameStateController.GameState.PLAYERSTART)
{
if (Vector3.Angle((gameObject.transform.position - spaceShip.position), spaceShip.transform.forward) > 90)
{
if ((gameObject.transform.position - spaceShip.position).sqrMagnitude > distanceToCull * distanceToCull)
{
KillEnemy();
return;
}
}
else
{
if (indicatorOff)
{
currentDistance = Vector3.Distance(missileTarget.position, transform.position);
if (currentDistance <= indicatorDist)
{
indicator.SetActive(true);
PlayAudioSound(indicatorClip);
indicatorOff = false;
}
}
//missileRigidbody.velocity = (transform.forward * missileVelocity) + (transform.right * (Mathf.Sin(Time.time) * wiggleMultiplyer)) + (transform.up * (Mathf.Cos(Time.time) * wiggleMultiplyer));
missileRigidbody.velocity = transform.forward * missileVelocity;
var rocketTargetRotation = Quaternion.LookRotation(missileTarget.position - transform.position);
missileRigidbody.MoveRotation(Quaternion.RotateTowards(transform.rotation, rocketTargetRotation, turn));
}
}
else
{
StartCoroutine(DestroyGameObject(gameObject, 0f));
}
}
void OnCollisionEnter(Collision hit)
{
switch (hit.gameObject.tag)
{
case "environment":
KillEnemy();
break;
case "enemy":
KillEnemy();
break;
case "enemyBullet":
break;
case "player":
CameraShake.Shake(1f, 0.25f);
float deathScale = Random.Range(5f * transform.localScale.x, 10f * transform.localScale.x);
GameObject fx = ObjectPooler.currentPool.enemyHits.Spawn();
if (fx == null) return;
fx.transform.position = transform.position;
fx.transform.rotation = Quaternion.identity;
fx.transform.parent = hit.transform;
fx.transform.localScale = new Vector3(deathScale, deathScale, deathScale);
fx.SetActive(true);
KillEnemy();
break;
case "playerBullet":
ProcessHit();
if (hits < 1)
{
LevelVariables.currentLevelVariables.scoreBoard.ScoreHit(scorePerDestroy);
KillEnemy();
}
break;
}
}
private void ProcessHit()
{
LevelVariables.currentLevelVariables.scoreBoard.ScoreHit(scorePerHit);
hits = hits - 1;
}
private void KillEnemy()
{
GameObject fx = ObjectPooler.currentPool.enemyDeath.Spawn();
if (fx == null) return;
fx.transform.position = transform.position;
fx.transform.rotation = Quaternion.identity;
fx.transform.parent = LevelVariables.currentLevelVariables.spawnParent;
float deathScale = 1f;
deathScale = Random.Range(1f * transform.localScale.x, 4f * transform.localScale.x);
fx.transform.localScale = new Vector3(deathScale, deathScale, deathScale);
fx.SetActive(true);
StartCoroutine(DestroyGameObject(gameObject, 0f));
}
public void PlayAudioSound(AudioClip clip)
{
LevelVariables.currentLevelVariables.computerAudioSource.volume = 1f;
LevelVariables.currentLevelVariables.computerAudioSource.PlayOneShot(clip);
}
IEnumerator DestroyGameObject(GameObject turnOffObject, float waitTime)
{
yield return new WaitForSeconds(waitTime);
trailRenderer.Clear();
trailRenderer.enabled = false;
turnOffObject.Recycle();
}
}
The section below is the code for creating the missile and is part of the enemy weapon firing script.
GameObject missileObj = ObjectPooler.currentPool.destroyerMissileAmmo.Spawn();
if (missileObj == null) yield break;
CreateMuzzleFlash(muzzleLocation);
//LevelVariables.currentLevelVariables.globalMissileAmount = LevelVariables.currentLevelVariables.globalMissileAmount + 1;
missileObj.transform.localScale = new Vector3(2f, 2f, 2f);
missileObj.transform.position = muzzleLocation.transform.position;
missileObj.transform.rotation = muzzleLocation.transform.rotation;
//missileObj.transform.LookAt(m_target.transform);
missileObj.SetActive(true);
I don't see any problems with this code that would cause a missile not to head towards the player, does anyone have any idea's
Im using Unity and have asked this on there forums however have not had any replies. Ive found this example from a plugin where I am trying to use the kinect to rotate an object using my right hand to rotate it right and the left to rotate left. I have managed to get the object to do this apart from it stops at each side of the object, but cant work out what part of the code is doing this.
Thanks
using UnityEngine;
using System.Collections;
using System;
public class GestureListener : MonoBehaviour, KinectGestures.GestureListenerInterface
{
// GUI Text to display the gesture messages.
public GUIText GestureInfo;
private bool raiselefthand;
private bool raiserighthand;
public bool IsSwipeLeft()
{
if(raiserighthand)
{
raiserighthand = false;
return true;
}
return false;
}
public bool IsSwipeRight()
{
if(raiselefthand)
{
raiselefthand = false;
return true;
}
return false;
}
public void UserDetected(uint userId, int userIndex)
{
// detect these user specific gestures
KinectManager manager = KinectManager.Instance;
manager.DetectGesture(userId, KinectGestures.Gestures.RaiseLeftHand);
manager.DetectGesture(userId, KinectGestures.Gestures.RaiseRightHand);
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = "Swipe left or right to change the slides.";
}
}
public void UserLost(uint userId, int userIndex)
{
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = string.Empty;
}
}
public void GestureInProgress(uint userId, int userIndex, KinectGestures.Gestures gesture,
float progress, KinectWrapper.NuiSkeletonPositionIndex joint, Vector3 screenPos)
{
// don't do anything here
}
public bool GestureCompleted (uint userId, int userIndex, KinectGestures.Gestures gesture,
KinectWrapper.NuiSkeletonPositionIndex joint, Vector3 screenPos)
{
string sGestureText = gesture + " detected";
if(GestureInfo != null)
{
GestureInfo.GetComponent<GUIText>().text = sGestureText;
}
if(gesture == KinectGestures.Gestures.RaiseRightHand)
raiserighthand = true;
else if(gesture == KinectGestures.Gestures.RaiseLeftHand)
raiselefthand = true;
return true;
}
public bool GestureCancelled (uint userId, int userIndex, KinectGestures.Gestures gesture,
KinectWrapper.NuiSkeletonPositionIndex joint)
{
// don't do anything here, just reset the gesture state
return true;
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PresentationScript : MonoBehaviour
{
public bool slideChangeWithGestures = true;
public bool slideChangeWithKeys = true;
public float spinSpeed = 5;
public bool autoChangeAlfterDelay = false;
public float slideChangeAfterDelay = 10;
public List<Texture> slideTextures;
public List<GameObject> horizontalSides;
// if the presentation cube is behind the user (true) or in front of the user (false)
public bool isBehindUser = false;
private int maxSides = 0;
private int maxTextures = 0;
private int side = 0;
private int tex = 0;
private bool isSpinning = false;
private float slideWaitUntil;
private Quaternion targetRotation;
private GestureListener gestureListener;
void Start()
{
// hide mouse cursor
Cursor.visible = false;
// calculate max slides and textures
maxSides = horizontalSides.Count;
maxTextures = slideTextures.Count;
// delay the first slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
targetRotation = transform.rotation;
isSpinning = false;
tex = 0;
side = 0;
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// get the gestures listener
gestureListener = Camera.main.GetComponent<GestureListener>();
}
void Update()
{
// dont run Update() if there is no user
KinectManager kinectManager = KinectManager.Instance;
if(autoChangeAlfterDelay && (!kinectManager || !kinectManager.IsInitialized() || !kinectManager.IsUserDetected()))
return;
if(!isSpinning)
{
if(slideChangeWithKeys)
{
if(Input.GetKeyDown(KeyCode.PageDown))
RotateToNext();
else if(Input.GetKeyDown(KeyCode.PageUp))
RotateToPrevious();
}
if(slideChangeWithGestures && gestureListener)
{
if(gestureListener.IsSwipeLeft())
RotateToNext();
else if(gestureListener.IsSwipeRight())
RotateToPrevious();
}
// check for automatic slide-change after a given delay time
if(autoChangeAlfterDelay && Time.realtimeSinceStartup >= slideWaitUntil)
{
RotateToNext();
}
}
else
{
// spin the presentation
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, spinSpeed * Time.deltaTime);
// check if transform reaches the target rotation. If yes - stop spinning
float deltaTargetX = Mathf.Abs(targetRotation.eulerAngles.x - transform.rotation.eulerAngles.x);
float deltaTargetY = Mathf.Abs(targetRotation.eulerAngles.y - transform.rotation.eulerAngles.y);
if(deltaTargetX < 1f && deltaTargetY < 1f)
{
// delay the slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
isSpinning = false;
}
}
}
private void RotateToNext()
{
// set the next texture slide
tex = (tex + 1) % maxTextures;
if(!isBehindUser)
{
side = (side + 1) % maxSides;
}
else
{
if(side <= 0)
side = maxSides - 1;
else
side -= 1;
}
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// rotate the presentation
float yawRotation = !isBehindUser ? 360f / maxSides : -360f / maxSides;
Vector3 rotateDegrees = new Vector3(0f, yawRotation, 0f);
targetRotation *= Quaternion.Euler(rotateDegrees);
isSpinning = true;
}
private void RotateToPrevious()
{
// set the previous texture slide
if(tex <= 0)
tex = maxTextures - 1;
else
tex -= 1;
if(!isBehindUser)
{
if(side <= 0)
side = maxSides - 1;
else
side -= 1;
}
else
{
side = (side + 1) % maxSides;
}
if(horizontalSides[side] && horizontalSides[side].GetComponent<Renderer>())
{
horizontalSides[side].GetComponent<Renderer>().material.mainTexture = slideTextures[tex];
}
// rotate the presentation
float yawRotation = !isBehindUser ? -360f / maxSides : 360f / maxSides;
Vector3 rotateDegrees = new Vector3(0f, yawRotation, 0f);
targetRotation *= Quaternion.Euler(rotateDegrees);
isSpinning = true;
}
}
I have managed to get the object to do this apart from it stops at each side of the object, but cant work out what part of the code is doing this.
I understand you ask for help finding which parts are involved in the rotation.
As such, I looked through the scripts and noticed 2 sections in the PresentationScript.
This part in the Update() method. Based on the code and the comments that are part of it.
// spin the presentation
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, spinSpeed * Time.deltaTime);
// check if transform reaches the target rotation. If yes - stop spinning
float deltaTargetX = Mathf.Abs(targetRotation.eulerAngles.x - transform.rotation.eulerAngles.x);
float deltaTargetY = Mathf.Abs(targetRotation.eulerAngles.y - transform.rotation.eulerAngles.y);
if(deltaTargetX < 1f && deltaTargetY < 1f)
{
// delay the slide
slideWaitUntil = Time.realtimeSinceStartup + slideChangeAfterDelay;
isSpinning = false;
}
This line in the Start() method is also involved.
targetRotation = transform.rotation;
A transform controls the position, scaling and as is relevant for this case, also the rotation of an object.