I made a test game in unity that makes it so when I click on a button, it spawns a cylinder created from a factory class. I'm trying to make it so when I create the cylinder, its height shrinks over the next 20 seconds. Some methods I found are difficult to translate into what I'm doing. If you could lead me to the right direction, I'd very much appreciate it.
Here's my code for the cylinder class
public class Cylinder : Shape
{
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.transform.localScale = new Vector3(1.0f, Random.Range(1, 2)-1*Time.deltaTime, 1.0f);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
}
This can be done with the Time.deltaTime and Vector3.Lerp in a coroutine function. Similar to Rotate GameObject over time and Move GameObject over time questions. Modified it a little bit to do just this.
bool isScaling = false;
IEnumerator scaleOverTime(Transform objectToScale, Vector3 toScale, float duration)
{
//Make sure there is only one instance of this function running
if (isScaling)
{
yield break; ///exit if this is still running
}
isScaling = true;
float counter = 0;
//Get the current scale of the object to be moved
Vector3 startScaleSize = objectToScale.localScale;
while (counter < duration)
{
counter += Time.deltaTime;
objectToScale.localScale = Vector3.Lerp(startScaleSize, toScale, counter / duration);
yield return null;
}
isScaling = false;
}
USAGE:
Will scale GameObject within 20 seconds:
StartCoroutine(scaleOverTime(cylinder.transform, new Vector3(0, 0, 90), 20f));
Check out Lerp. A general example of how to use it would be something like this:
float t = 0;
Update()
{
t += Time.deltaTime;
cylinder.localScale = new Vector3(1, Mathf.Lerp(2f, 1f, t/3f), 1); // shrink from 2 to 1 over 3 seconds;
}
You will create a new monobehaviour script and add it to your primitive. Then you wil use "Update" method of monobehaviour (or use coroutine) for change object over time.
Monobehaviour must be look like this:
public class ShrinkBehaviour : MonoBehaviour
{
bool isNeedToShrink;
Config currentConfig;
float startTime;
float totalDistance;
public void StartShrink(Config config)
{
startTime = Time.time;
currentConfig = config;
totalDistance = Vector3.Distance(currentConfig.startSize, currentConfig.destinationSize);
isNeedToShrink = true;
transform.localScale = config.startSize;
}
private void Update()
{
if (isNeedToShrink)
{
var nextSize = GetNextSize(currentConfig);
if (Vector3.Distance(nextSize, currentConfig.destinationSize) <= 0.05f)
{
isNeedToShrink = false;
return;
}
transform.localScale = nextSize;
}
}
Vector3 GetNextSize(Config config)
{
float timeCovered = (Time.time - startTime) / config.duration;
var result = Vector3.Lerp(config.startSize, config.destinationSize, timeCovered);
return result;
}
public struct Config
{
public float duration;
public Vector3 startSize;
public Vector3 destinationSize;
}
}
For using this, you must do next:
public Cylinder()
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
var shrink = cylinder.AddComponent<ShrinkBehaviour>();
shrink.StartShrink(new ShrinkBehaviour.Config() { startSize = Vector3.one * 10, destinationSize = Vector3.one * 1, duration = 20f });
cylinder.transform.position = new Vector3(3, 0, 0);
cylinder.GetComponent<MeshRenderer>().material.color = Random.ColorHSV();
Destroy(cylinder, 30.0f);
}
You must remember, monobehaviour-script must be in separate file, and must have name similar to monobehaviour-class name. For example, ShrinkBehaviour.cs;
Related
I am trying to make a grappling hook more fluent but as of right now it is very choppy and does not have the right feel. It currently makes a line and pulls the player there. I have not tried anything yet because I am not even sure we're to start on fixing this. Here is all the grappling code below. `using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SFPSC_PlayerMovement))] // PlayerMovement also requires Rigidbody
public class SFPSC_GrapplingHook : MonoBehaviour
{
public bool IsGrappling
{
get { return isGrappling; }
}
private SFPSC_PlayerMovement pm;
private Rigidbody rb;
private int segments;
private void Start()
{
segments = rope.segments;
pm = this.GetComponent<SFPSC_PlayerMovement>();
rb = this.GetComponent<Rigidbody>();
}
private bool isGrappling = false;
private void Update()
{
if (crossHairSpinningPart != null)
{
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
goto _else;
crossHairSpinningPart.gameObject.SetActive(true);
crossHairSpinningPart.Rotate(Vector3.forward * crossHairSpinSpeed * Time.deltaTime);
goto _out;
}
}
_else:
crossHairSpinningPart.gameObject.SetActive(false);
}
_out:
if (!isGrappling)
{
if (Input.GetKeyDown(SFPSC_KeyManager.Grapple))
Grapple();
return;
}
else
{
if (!Input.GetKey(SFPSC_KeyManager.Grapple))
UnGrapple();
GrappleUpdate();
return;
}
}
[Header("Properties")]
public float maxGrappleDistance = 100.0f;
public SFPSC_Rope rope;
public float maximumSpeed = 100.0f;
public float deceleration = 2500.0f; // This is how much the player is going to decelerate after stopped grappling
public float deceleratingTime = 1.4f; // This is the time the decelerating is going to act on the player after stopped grappling
public RectTransform crossHairSpinningPart;
public float crossHairSpinSpeed = 200.0f;
public float distanceToStop = 2.0f;
public LayerMask layerMask;
public float grappleCooldown = 1.0f;
private bool isBlocked = false;
private Transform location; // the grappled location
private RaycastHit hitInfo;
private string hitName;
public void Grapple()
{
if (isBlocked)
return;
// we need 2 raycasts bc w/ 1 you can grapple through colliders which isn't good
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance, layerMask))
{
hitName = hitInfo.collider.name;
if (Physics.Raycast(SFPSC_FPSCamera.cam.transform.position, SFPSC_FPSCamera.cam.transform.forward, out hitInfo, maxGrappleDistance))
{
if (hitName != hitInfo.collider.name)
return;
// We create a GameObject and we parent it to the grappled object.
// If we don't parent it to the object and the object moves the player is stuck only on one location instead of the moving object.
location = new GameObject().transform;//Instantiate(new GameObject(), hitInfo.point, Quaternion.identity).transform;
location.position = hitInfo.point;
location.parent = hitInfo.collider.transform;
if (decelerateTimer != 0.0f)
StopCoroutine(Decelerate());
pm.DisableMovement();
// Rope attaching
rope.segments = (int)((hitInfo.distance / maxGrappleDistance) * segments);
rope.Grapple(transform.position, hitInfo.point);
rb.useGravity = false;
isGrappling = true;
}
}
}
private Vector3 grappleForce;
public void UnGrapple()
{
if (!isGrappling)
return;
if (location != null)
Destroy(location.gameObject);
if (decelerateTimer == 0.0f)
StartCoroutine(Decelerate());
else
decelerateTimer = 0.0f;
pm.EnableMovement();
// Rope detaching
rope.UnGrapple();
Invoke("UnblockGrapple", grappleCooldown);
rb.useGravity = true;
isGrappling = false;
}
private void UnblockGrapple()
{
isBlocked = false;
}
private float decelerateTimer = 0.0f, max;
private IEnumerator Decelerate()
{
WaitForEndOfFrame wfeof = new WaitForEndOfFrame();
max = deceleratingTime * Mathf.Clamp01(targetDistance / 10.0f) * Mathf.Clamp01(rb.velocity.magnitude / 30.0f);
for (; decelerateTimer < max; decelerateTimer += Time.deltaTime)
{
rb.AddForce(-rb.velocity.normalized * deceleration * (1.0f - decelerateTimer / max) * Mathf.Clamp01(rb.velocity.sqrMagnitude / 400.0f) * Time.deltaTime, ForceMode.Acceleration);
yield return wfeof;
}
decelerateTimer = 0.0f;
}
private Vector3 dir;
private float speed = 0.0f, targetDistance;
private void GrappleUpdate()
{
if (location == null)
return;
targetDistance = Vector3.Distance(transform.position, location.position);
rope.segments = (int)((targetDistance / maxGrappleDistance) * segments);
dir = (location.position - transform.position).normalized;
rb.velocity = Vector3.Lerp(rb.velocity, dir * maximumSpeed * Mathf.Clamp01(targetDistance / (4.0f * distanceToStop)), Time.deltaTime);
// Rope updating
rope.UpdateStart(transform.position);
rope.UpdateGrapple();
}
private Vector3 ClampMag(Vector3 vec, float maxMag)
{
if (vec.sqrMagnitude > maxMag * maxMag)
vec = vec.normalized * maxMag;
return vec;
}
}
`
Try using FixedUpdate instead of Update for physics based work (basically all of your code in Update right now). Update is dependent on your computer's clock speed and refresh rate (more or less), and gets called at fairly irregular intervals, because the next update is called in the next frame, after the present frame has finished processing. FixedUpdate makes it frame-rate independent.
Also, you can cap your framerate using Application.targetFrameRate and cap it to a decent FPS.
You could also multiply your movement with Time.deltaTime for smoother movement, although this is a standard practice and yet debatable for use as a smoothing value.
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
public class Waypoints : UnityEngine.MonoBehaviour
{
public List<Transform> waypoints = new List<Transform>();
public float movementSpeed = 5.0f;
public float slowdownSpeed = 1f;
public float rotationSpeed = 2.0f;
public float waypointDistance = 0.1f;
public float slowdownDistance = 7f;
public bool moveBackward = false;
public bool moveLoop = false;
public bool includeTransformPosition = false;
private Transform targetWaypoint;
private int targetWaypointIndex = 0;
private int lastWaypointIndex;
private bool includeTransform = true;
private GameObject go;
// Use this for initialization
void Start()
{
go = new GameObject();
go.transform.position = transform.position;
if (moveBackward && waypoints.Count > 2)
{
lastWaypointIndex = 0;
targetWaypoint = waypoints[waypoints.Count - 1];
}
else
{
lastWaypointIndex = waypoints.Count - 1;
targetWaypoint = waypoints[targetWaypointIndex]; //Set the first target waypoint at the start so the enemy starts moving towards a waypoint
}
}
// Update is called once per frame
void Update()
{
if (includeTransformPosition && includeTransform)
{
waypoints.Insert(0,go.transform);
includeTransform = false;
}
else
{
if (includeTransformPosition == false)
{
waypoints.Remove(go.transform);
includeTransform = true;
}
}
float movementStep = movementSpeed * Time.deltaTime;
float rotationStep = rotationSpeed * Time.deltaTime;
Vector3 directionToTarget = targetWaypoint.position - transform.position;
Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget);
transform.rotation = Quaternion.Slerp(transform.rotation, rotationToTarget, rotationStep);
float distance = Vector3.Distance(transform.position, targetWaypoint.position);
CheckDistanceToWaypoint(distance);
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);
}
void CheckDistanceToWaypoint(float currentDistance)
{
if (currentDistance <= waypointDistance)
{
targetWaypointIndex++;
UpdateTargetWaypoint();
}
}
void UpdateTargetWaypoint()
{
if (targetWaypointIndex > lastWaypointIndex)
{
targetWaypointIndex = 0;
}
targetWaypoint = waypoints[targetWaypointIndex];
}
}
At this part I'm trying to slowdown the movement speed but it's not changing the speed at all :
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
What I'm trying to do when the transform start to move increase the speed slowly to some constant speed and then when the transform is getting closer to the waypoint then if the distance is less then 7 decrease the speed down to 0 so the object will stop at the waypoint then after X seconds move back the transform to the transform original position(go.transform) with the same increasing decreasing speed movement.
but I can't even make the first simple slowdown.
you set slowdownDistance to 7, i dont see you ever reducing it below 7 but you have if statement that executes only it it is under 7 do you reduce it elsewhere?
You probably planned to compare slowdownDistance to some distance rather than comparing it to its own value.
If you want to slow down the whole scene you could use a:
while(distance < range)
{
Time.timeScale = 1 - (distance / value);
}
with this you can change the value and so when the player come close time will be slown down mostly.
With the same idea you can do:
while(distance < range)
{
speed = speedInitial - (distance / value);
}
If you want to change just the speed of the player
I am trying to rotate the hand of the clock on click, but nothing is actually happening, none of the arrows is actually moving so I probably did something wrong... any help would be much appreciated, thank you
script below
public class Clock : MonoBehaviour {
public GameObject minuteHand;
public GameObject hourHand;
private Quaternion targetRotation;
float speed = 0.1f;
void Start()
{
targetRotation = transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
minuteHand.transform.rotation = Quaternion.Lerp(minuteHand.transform.rotation, targetRotation, Time.deltaTime * speed);
}
} }
Your code is only executed exactly in one single frame when the mouse button goes down.
Also that is not how Lerp works. Lerp expects a factor between 0 and 1 and interpolates both values. If you multiply your speed (0.1) and the Time.deltaTime which for 60FPS has a value of about 1/60f = 0.017f you get a resulting factor of about 0.0017f => You will always stay pretty close to the first value
especially since additionally your targetRotation always equals the current transform.rotation!
I assume what you wanted is: Every click move the minute 6° ahead (360°/60 minutes = 6°/minute).
Once a full circle is done move the hour 30° ahead (360°/12 hours = 30°/hour)
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
// Store original orientations
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// First calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12)
{
hours = 0;
}
}
// Now update the rotations with the new time
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
minuteHand.transform.rotation = targetMinuteRotation;
hourHand.transform.rotation = targetHourRotation;
}
}
}
This would make the clock "jump" to the target rotations. If what you rather wanted is a smooth rotation (what I assume from the usage of Lerp) I would probably use a Coroutine for that:
public class Clock : MonoBehaviour
{
public GameObject minuteHand;
public GameObject hourHand;
float speed = 0.1f;
Quaternion originalRotationMinute;
Quaternion originalRotationHour;
int minutes;
int hours;
void Start()
{
originalRotationMinute = minuteHand.transform.rotation;
originalRotationHour = hourHand.transform.rotation;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// cancel the current routines
StopAllCoroutines();
// calculate the new time
minutes++;
if(minutes == 60)
{
minutes = 0;
hours++;
if(hours = 12) hours = 0;
}
// I'm assuming here you want to rotate around the Z axis
// if this is not the case change this according to your needs
var targetMinuteRotation = originalRotationMinute * Quaternion.Euler(0, 0, 6.0f * minutes);
var targetHourRotation = originalRotationHour * Quaternion.Euler(0, 0, 30.0f * hours);
// This time instead of directly setting the values start coroutines
// to rotate the hands smoothly according to the given speed
StartCoroutine (RotateTo(minuteHand, targetMinuteRotation));
StartCoroutine (RotateTo(hourHand, targetHourRotation));
}
}
// rotate the given object to the given target rotation
// according to the given speed
private IEnumerator RotateTo(GameObject obj, Quaternion targetRotation)
{
var targetTransform = obj.transform;
var current = targetTransform.rotation;
// I your are not rotating on the Z axis change this accordingly to your needs
var difference = targetRotation.eulerAngels.z - current.eulerAngles.z;
if(Mathf.Approximately(0, difference)) yield break;
var duration = difference / speed;
var timePassed = 0f;
while(timePassed <= duration)
{
var factor = timePassed / duration;
targetTransform.rotation = Quaternion.Lerp(current, targetRotation, factor);
timePassed += Time.deltaTime;
yield return null;
}
// t be sure set the final rotation once in the end
targetTransform.rotation = targetRotation;
}
}
Typed on smartphone but I hope the idea gets clear
For me only this works. You can't modify speed to make it even slower, but it looks fine (may be it will be helpful to someone)
float speed = 0.1f //how fast rotate
private void FixedUpdate()
{
nextRot = hour * -30;
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, nextRot), speed);
}
In one of my games, a snake like, I have a bug where the parts are supposed to be 0.125f apart, but instead, the the distance between is the same as the distance between the first and second part.
What I believe to be the relevant code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SnakeHead : MonoBehaviour
{
public float growTime = 5;
private float moveSpeed;
private Transform origin, prevPart = null;
private List<GameObject> snakeBodyPartsList;
public Mesh mesh;
public Material material;
private GameObject body, sphere;
private RaycastHit hit;
void Start()
{
sphere = GameObject.Find("Sphere");
origin = new GameObject("SnakeHead Origin").transform;
origin.parent = sphere.transform;
transform.parent = origin;
snakeBodyPartsList = new List<GameObject>();
snakeBodyPartsList.Add(gameObject);
StartCoroutine("addBodyPart");
}
void Update()
{
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
foreach (GameObject part in snakeBodyPartsList)
{
moveSpeed = 30 + (snakeBodyPartsList.Count * 2.5f);
if (part == gameObject)
{
if (Input.GetKey("a") || Input.GetKey("d"))
{
var turnRate = Input.GetKey("a") ? -120 : 120;
part.transform.parent.Rotate(0, 0, turnRate * Time.deltaTime);
}
part.transform.rotation = Quaternion.LookRotation(transform.forward, part.transform.position - sphere.transform.position);
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, part.transform.forward)) * part.transform.parent.rotation;
}
else
{
part.transform.rotation = Quaternion.LookRotation(prevPart.transform.position, part.transform.position - sphere.transform.position);
if (prevPart != null && Vector3.Distance(part.transform.position, prevPart.position) > 0.125f)
{
// moveSpeed = moveSpeed + Vector3.Distance(part.transform.position, prevPart.position)* 100;
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, -(prevPart.position - part.transform.position).normalized * 0.125f)) * part.transform.parent.rotation;
}
}
returnToSurface(part);
prevPart = part.transform;
}
}
private IEnumerator addBodyPart()
{
yield return new WaitForSeconds(1);
body = createNewGameObject(body, "Body " + snakeBodyPartsList.Count, null, mesh, material, snakeBodyPartsList[snakeBodyPartsList.Count - 1].transform.position, Vector3.one / 10, true, true);
snakeBodyPartsList.Add(body);
yield return new WaitForSeconds(Mathf.Abs(growTime - 1));
StartCoroutine("addBodyPart");
}
public GameObject createNewGameObject(GameObject uGO, string Name, Transform Parent, Mesh Mesh, Material Material, Vector3 Position, Vector3 localScale, bool needsOrigin, bool needscollider)
{
uGO = new GameObject(Name);
if (needsOrigin)
{
origin = new GameObject("BodyPart Origin " + snakeBodyPartsList.Count).transform;
origin.parent = sphere.transform;
uGO.transform.parent = origin;
}
else
{
uGO.transform.parent = Parent;
}
uGO.gameObject.AddComponent<MeshFilter>().mesh = Mesh;
uGO.AddComponent<MeshRenderer>().material = Material;
uGO.transform.position = Position;
uGO.transform.localScale = localScale;
if (needscollider)
{
uGO.AddComponent<BoxCollider>().size = Vector3.one;
uGO.GetComponent<BoxCollider>().isTrigger = true;
}
uGO.transform.forward = transform.forward;
uGO.transform.rotation = transform.rotation;
return uGO;
}
void returnToSurface(GameObject a)
{
if (Vector3.Distance(a.transform.position, sphere.transform.position) > 1.05)
{
while (Vector3.Distance(a.transform.position, sphere.transform.position) >= 1.045)
{
a.transform.position = new Vector3(a.transform.position.x, a.transform.position.y, a.transform.position.z - 0.001f);
}
}
else if (Vector3.Distance(a.transform.position, sphere.transform.position) < 1.04)
{
while (Vector3.Distance(a.transform.position, sphere.transform.position) <= 1.045)
{
a.transform.position = new Vector3(a.transform.position.x, a.transform.position.y, a.transform.position.z + 0.001f);
}
}
}
public List<GameObject> getPartsList()
{
return snakeBodyPartsList;
}
}
The movement of the block is probably badly coded because I am fairly new, any general tips and cleanup is generally welcome also. The blocks are moving around a sphere hence the perhaps odd seeming movement code.
I might be wrong about this, but I don't have enough rep to comment, so I'll post it as a reply for you to try:
Since your issue is only between the head and first body part, I'm imagining your issue is because there's no prevPart set on the first run (the gameObject body part isn't necessarily the first one to be checked in the foreach). You might want to try handling the first if-statement before the foreach, so that everything is guaranteed to be set up properly for the first run. You can keep the gameObject body part out of the list or handle the case with an if (like in this example).
Something like this:
void Update()
{
moveSpeed = 30 + (snakeBodyPartsList.Count * 2.5f);
// First update the head (gameObject)
if (Input.GetKey("a") || Input.GetKey("d"))
{
var turnRate = Input.GetKey("a") ? -120 : 120;
transform.parent.Rotate(0, 0, turnRate * Time.deltaTime);
}
transform.rotation = Quaternion.LookRotation(transform.forward, transform.position - sphere.transform.position);
transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(transform.parent.position - transform.position, transform.forward)) * transform.parent.rotation;
returnToSurface(part);
prevPart = part.transform;
// Then update the body (snakeBodyPartsList)
foreach (GameObject part in snakeBodyPartsList)
{
if (part != gameObject)
{
if (prevPart != null) // prevPart is already used here, so you might as well check it here instead of after the next line
{
part.transform.rotation = Quaternion.LookRotation(prevPart.transform.position, part.transform.position - sphere.transform.position);
if (Vector3.Distance(part.transform.position, prevPart.position) > 0.125f)
{
part.transform.parent.rotation = Quaternion.AngleAxis(moveSpeed * Time.deltaTime, Vector3.Cross(part.transform.parent.position - part.transform.position, -(prevPart.position - part.transform.position).normalized * 0.125f)) * part.transform.parent.rotation;
}
}
else
{
Debug.Log('No prevPart in the snakeBodyPartsList foreach'); // EDIT: Might as well add something here just in case... :)
}
returnToSurface(part);
prevPart = part.transform;
}
}
}
Does that help?
PS: The movement code looks fine to me, but you might want to give your magic numbers variable names for clarity.
So, this was solved by halving the move speed of the head during turning, havnt figure out why but the head moves faster than the body when turning causing it to pull away from the body parts. Thanks #Bruins from solent uni.
I have a scene in which the player can pick up and drop objects, as well as move and look around.
All player objects are children of an empty game object "MainCharacter":
MainCharacter >
Capsule (With RigidBody and PlayerMoveScript) >
PlayerBase (empty - used for checking if grounded)
MainCamera >
Hands(With PickUpDrop script)
The object I pick up Lerps to my Hands position, however after my capsule collides with any walls there is a strange jittering which I cannot work out how to fix!!
Heres the .exe:GameTest
Heres the data folder : Data
Here are the scripts:
Pick Up and Drop Script:
public bool handsFull = false;
public float distanceMax = 20f;
public Transform handPosition;
public LayerMask canPickUp;
public GameObject taggedGameObject;
public bool colliderTriggered;
public bool bounds;
public PickedUpObject pickedUpScript;
public Rigidbody player;
// Use this for initialization
void Start () {
print(FindClosestPickup().name);
handPosition = transform;
pickedUpScript = null;
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.E) && !bounds) {
if (Physics.CheckSphere (handPosition.position, 2f, canPickUp)) {
if (handsFull) {
Drop ();
}
if (!handsFull) {
PickedUp ();
}
handsFull = !handsFull;
}
}
if (handsFull) {
RotateMovePickedUpObject();
}
}
private void PickedUp(){
//Closest object to top of list
taggedGameObject = (GameObject)FindClosestPickup();
taggedGameObject.collider.isTrigger = true;
taggedGameObject.rigidbody.useGravity = false;
taggedGameObject.rigidbody.isKinematic = true;
pickedUpScript = taggedGameObject.GetComponent<PickedUpObject> ();
Debug.Log ("Pick Up");
}
private void RotateMovePickedUpObject(){
//Rotate
if(Input.GetKeyDown(KeyCode.End)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(0, 0, 45);
}
if(Input.GetKeyDown(KeyCode.Delete)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(0, 45, 0);
}
if(Input.GetKeyDown(KeyCode.PageDown)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(0, -45, 0);
}
if(Input.GetKeyDown(KeyCode.Home)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(0, 0, -45);
}
if(Input.GetKeyDown(KeyCode.PageUp)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(-45, 0, 0);
}
if(Input.GetKeyDown(KeyCode.Insert)){
taggedGameObject.transform.localRotation *= Quaternion.Euler(45, 0, 0);
}
taggedGameObject.transform.position = Vector3.Lerp(taggedGameObject.transform.position, handPosition.position, (1 - Mathf.Exp( -20 * Time.smoothDeltaTime )) * 10);
}
private void Drop(){
taggedGameObject.collider.isTrigger = false;
taggedGameObject.rigidbody.useGravity = true;
taggedGameObject.rigidbody.isKinematic = false;
taggedGameObject = null;
Debug.Log ("Drop");
pickedUpScript = null;
}
private GameObject FindClosestPickup() {
//Find closest gameobject with tag
GameObject[] gos;
gos = GameObject.FindGameObjectsWithTag("pickup");
GameObject closest = null;
float distance = Mathf.Infinity;
Vector3 position = transform.position;
foreach (GameObject go in gos) {
Vector3 diff = go.transform.position - position;
float curDistance = diff.sqrMagnitude;
if (curDistance < distance) {
closest = go;
distance = curDistance;
}
}
return closest;
}
}
The Picked Up Objects Script:
public PickUpDrop pickUpScript;
public GameObject thisOne;
public Color thecolor;
public bool inObject;
// Use this for initialization
void Start () {
thisOne = this.gameObject;
}
// Update is called once per frame
void Update ()
{
thecolor = thisOne.renderer.material.color;
if (pickUpScript.taggedGameObject != thisOne)
{
gameObject.renderer.material.color = Color.gray;
}
if (pickUpScript.taggedGameObject == thisOne)
{
Color color = renderer.material.color;
color.a = 0.5f;
renderer.material.color = color;
}
}
void OnTriggerEnter ()
{
if (thisOne == pickUpScript.taggedGameObject)
{
inObject = true;
pickUpScript.bounds = true;
gameObject.renderer.material.color = Color.red;
}
}
void OnTriggerExit()
{
if(thisOne == pickUpScript.taggedGameObject)
{
inObject = false;
pickUpScript.bounds = false;
gameObject.renderer.material.color = Color.gray;
}
}
}
taggedGameObject.transform.position = Vector3.Lerp(taggedGameObject.transform.position, handPosition.position, (1 - Mathf.Exp( -20 * Time.smoothDeltaTime )) * 10);
This line will keep moving the object towards the hand's position. If you have a rigidbody attached to the game object you're moving then the physics acting on that object during the physics calculation will conflict with the manual movement of the object during the Update function.
It depends on what you would like to happen when this conflict occurs as to the solution. If you simply want the 'jittering' to stop and still be able to hold objects against other physical objects, then use this;
taggedGameObject.rigidbody.AddForce( ( taggedGameObject.transform.position - handPosition.position ) * force );
This will keep all interactions with the rigidbody. You'll have to tweak the force you move the object with, and perhaps disable gravity on the tagged game object while it's in the players hands. But it should have the desired effect.