Floating Objects in Unity - unity3d

I found this code on a web tutorial to make objects float on water in Unity:
RequireComponent (typeof (Rigidbody))]
public class ObjectFloatScript : MonoBehaviour {
public float waterLevel = 0.0f;
public float floatThreshold = 2.0f;
public float waterDensity = 0.125f;
public float downForce = 4.0f;
private float forceFactor;
private Vector3 floatForce;
void FixedUpdate () {
forceFactor = 1.0f - ((transform.position.y - waterLevel) / floatThreshold);
if (forceFactor > 0.0f) {
floatForce = -Physics.gravity * GetComponent<Rigidbody> ().mass * (forceFactor - GetComponent<Rigidbody> ().velocity.y * waterDensity);
GetComponent<Rigidbody> ().AddForceAtPosition (floatForce, transform.position);
}
}
}
What I don't Understand is the relationship between the Rigidbody.velocity.y and the water density ? Can someone help ?
Thanks

It looks like some kind of "belly flop" factor :)
Only having -gravity * mass would result in a force perfectly countering the gravitation force on the object, stopping it instantly.
By scaling this force down by velocity.y * waterDensity, it will reduce this countering force, based on the entry velocity, simulating some kind of "water friction" as the object enters the water.
Cheers!

Related

Why is my Controller drifting with Slerp?

I've been working on this Controller, where I walk around a small planet. I'm using Quaternion.Slerp, and when I'm at certain points on the planet, the controller slowly drifts, rotating around the Y axis. My thought is that it is holding a value based on my starting position, and so when I move to different points, the Slerp function is trying to spin me toward that location? I've tried messing around with the script, moving different portions to their own custom methods to pinpoint the issue, but I'm a bit lost at this point. Any help would be appreciated!
I think the issue is going to be somewhere in the bottom two methods NewRotation, or RunWalkStand.
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] private LayerMask _groundMask;
[SerializeField] private Transform _groundCheck;
[SerializeField] private Transform cam;
public float jumpCooldown = 1f;
public float groundCheckRadius = 0.3f;
private float speed = 8;
private float runSpeed = 2;
private float turnSpeed = 800f;
private float jumpForce = 500f;
private Rigidbody rb;
private Vector3 direction;
private GravityBody _gravityBody;
private Animator playerAnimator;
private GameObject planetRecall;
void Start()
{
_gravityBody = transform.GetComponent<GravityBody>();
playerAnimator = GetComponent<Animator>();
planetRecall = GameObject.FindGameObjectWithTag("Planet Recall");
}
void Update()
{
bool isCloseToGround = Physics.CheckSphere(_groundCheck.position, groundCheckRadius, _groundMask);
NewRotation();
if (Input.GetKeyDown(KeyCode.Space) && isCloseToGround)
{
Jump();
}
}
void FixedUpdate()
{
RunWalkStand();
}
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject == planetRecall)
{
playerAnimator.SetBool("Grounded", true);
}
}
private void Jump()
{
rb.AddForce(-_gravityBody.GravityDirection * jumpForce, ForceMode.Impulse);
playerAnimator.SetTrigger("Fly_trig");
playerAnimator.SetBool("Grounded", false);
}
private void NewRotation()
{
rb = transform.GetComponent<Rigidbody>();
Vector3 mouseRotationY = new Vector3(0f, Input.GetAxisRaw("Mouse X"), 0f);
Quaternion rightDirection = Quaternion.Euler(0f, mouseRotationY.y * (turnSpeed * Time.deltaTime), 0f).normalized;
Quaternion newRotation = Quaternion.Slerp(rb.rotation, rb.rotation * rightDirection, Time.deltaTime * 1000f);
rb.MoveRotation(newRotation);
//Move Side to Side
Vector3 sideToSide = transform.right * Input.GetAxisRaw("Horizontal");
rb.MovePosition(rb.position + sideToSide * (speed * Time.deltaTime));
}
private void RunWalkStand()
{
direction = new Vector3(0f, 0f, Input.GetAxisRaw("Vertical")).normalized;
Vector3 forwardDirection = transform.forward * direction.z;
bool isRunning = direction.magnitude > 0.1f;
//Walking
if (isRunning && !Input.GetKey(KeyCode.LeftShift))
{
rb.MovePosition(rb.position + forwardDirection * (speed * Time.deltaTime));
playerAnimator.SetFloat("Speed_f", 0.5f);
}
//Running
else if(isRunning && Input.GetKey(KeyCode.LeftShift))
{
rb.MovePosition(rb.position + forwardDirection * (speed * runSpeed * Time.deltaTime));
playerAnimator.SetFloat("Speed_f", 1f);
}
//Standing
else if(isRunning == false)
{
playerAnimator.SetFloat("Speed_f", 0f);
}
}
}
`
it might be because a slerp is not a lineair line so its velocity is less when you are close to the endpoint maybe try Quaternion.RotateTowards.
I "Mostly" solved this issue, and it's an unexpected solution. The drift was solved by freezing rotation on the player in the inspector (Still don't know what was causing the player to spin though) However this caused extra jitter. I found two issues.
If I've selected ANY game object in the hierarchy, and play the game, the game is jittery, but if I click off of it onto nothing, the game runs smoother (Not completely better, but much smoother).
I'm using a Gravity script for the planet that applies gravity to the player's Rigidbody, and I believe with multiple things acting on the RB at the same time, it causes jitter. I'm thinkin of trying to greatly simplify the project, but putting the different methods from the scripts into different Update methods helps a good bit depending on the combination (Not as simple as Physics movement in FixedUpdate and camera in LateUpdate unfortunately).

Unity: how to force rotation after rotating towards a direction?

After asking here for a solution, which worked, my sphere "walks" to each point of a list. Now, I'm trying to "force" a rotation around the x axis, to fake the ball is "rolling". I came up with the following code, but it doesn't work (it makes a progressive acceleration of the rotation on the x-axis, which I both dont want and... dont understand).
Create a new project, add a sphere and add this small script. Then add 2 points in the points list. This script does nothing but moving the object (the sphere) each points of the list, and make a simple rotation around the x-axis.
Simple rotation around the x-axis is not realistic (it's working as long as the sphere doesn't change its direction, but in this case, it should rotate on x + on z, but I dont know how to do this too...) so if you have a more realistic idea its even more welcome.
using UnityEngine;
public class MoveAlongPath : MonoBehaviour
{
public Vector3[] points;
public float speed = 1.0f;
public float rotateSpeed = 200.0f;
public float minDistance = Vector3.kEpsilon;
private SphereCollider _c;
[SerializeField] private Vector3 _posStart;
[SerializeField] private Vector3 _posEnd;
[SerializeField] private int _currentIndex;
[SerializeField] private Rigidbody _rigidbody;
[SerializeField] private Vector3 _direction;
[SerializeField] private Vector3 _directionNormalized;
[SerializeField] private float _xRotation;
private void NextPoint()
{
_posStart = points[_currentIndex];
transform.position = _posStart;
_currentIndex = ++_currentIndex % points.Length;
_posEnd = points[_currentIndex];
_direction = _posEnd - _posStart;
_directionNormalized = _direction.normalized;
}
private void Start()
{
_rigidbody = GetComponent<Rigidbody>();
NextPoint();
}
private void FixedUpdate()
{
_xRotation = (_xRotation + -10 * Time.fixedDeltaTime) % 360;
Quaternion q = Quaternion.LookRotation(
_directionNormalized, Vector3.up
);
transform.rotation = Quaternion.RotateTowards(
transform.rotation, q, rotateSpeed * Time.fixedDeltaTime
) * Quaternion.AngleAxis(_xRotation, Vector3.left);
Vector3 step = speed * Time.fixedDeltaTime * _directionNormalized;
if (step.magnitude > _direction.magnitude) {
step = _direction; // don't overshoot
}
Vector3 newPos = transform.position + step;
_rigidbody.MovePosition(newPos);
if (Vector3.Distance(_posEnd, transform.position) < minDistance) {
/* close enough -> next point: */
NextPoint();
}
}
}

Character Walks backwards with no reason (Brackeys First Person Character Controller in Unity)

I use the First Person Character Controller Script from Brackeys. But sometimes, my character walks backwards with no button pressed. I've tried to change the values of the Character Controller and to change the values of the axis in the Project Settings, but nothing really helped.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementScript : MonoBehaviour
{
public CharacterController controller;
public Transform groundcheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
public float speed = 12f;
public float gravity = -9.81f;
public float jumpheight = 3f;
Vector3 velocity;
bool isGrounded;
void Start()
{
}
void Update()
{
isGrounded = Physics.CheckSphere(groundcheck.position, groundDistance, groundMask);
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y += Mathf.Sqrt(jumpheight * -2f * gravity);
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
I really dont know what the problem is, but maybe you can help me
I see nothing wrong with the script. Something must be giving a vertical input outside the script.
If you're using a controller, your left joystick might have some drift issues. Adding some deadzone to your movement vector can prevent any involuntary movement from happening. For example, you can check if your input is inferior to a certain threshold and set it to zero if that's the case.
Here's a simple way to do it (in this example, the threshold is 0.125. If the joystick drift still gives an input, try an higher value.):
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
if(x <= 0.125f)
x = 0
if(y <= 0.125f)
y = 0
Little late here but I had the same issue.
What caused it was having another controller plugged in besides mouse and keyboard, in my case a steering wheel. So make sure all extra controllers are unplugged

How to identify multiple objects in a sphere cast?

I am trying to implement a RADAR sensor in unity. I am currently using sphere cast to identify the object and measure its velocity and distance. At the moment even though there are several objects in within the radius of the sphere cast ,It only identifies the nearest one. How can i make it recognize all the objects within the sphere cast.?
Any help would be appreciated..
This is what i have done now which just identifies identify the closest object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class spherecast : MonoBehaviour
{
Rigidbody rb;
public GameObject curobject;
public float radius;
public float maxdist;
public LayerMask layermask;
public float velocity;
public Time deltatime;
public Vector3 previous;
private Vector3 origin;
private Vector3 direction;
private float hitdist;
// Use this for initialization
void Start()
{
previous = curobject.transform.position;
}
// Update is called once per frame
void Update()
{
velocity = hitdist/Time.deltaTime;
origin = transform.position;
direction = transform.forward;
RaycastHit hit;
if (Physics.SphereCast(origin, radius, direction, out hit, maxdist, layermask, QueryTriggerInteraction.UseGlobal))
{
curobject = hit.transform.gameObject;
hitdist = hit.distance;
Debug.Log("Distance" + hitdist);
velocity = ((curobject.transform.position - previous).magnitude) / Time.deltaTime;
previous = curobject.transform.position;
Debug.Log("Velocity" + velocity);
// Debug.Log("Velocity = " + velocity);
}
else
{
hitdist = maxdist;
curobject = null;
}
}
private void ondrawgizmosselected()
{
Gizmos.color = Color.red;
Debug.DrawLine(origin, origin + direction * hitdist);
Gizmos.DrawWireSphere(origin + direction * hitdist, radius);
}
}
How can i make it recognize all the objects within the sphere cast.?
I believe you need to use SphereCastAll instead of SphereCast for that.
https://docs.unity3d.com/ScriptReference/Physics.SphereCastAll.html
Answer:
"Like Physics.SphereCast, but this function will return all hits the sphere sweep intersects."
It returns a RaycastHit[].

Single object 3D viewer for Google Cardboard with Unity 5

I'm trying to recreate the functionality of the Google Cardboard app’s 'Exhibit' demo. i.e. viewing a single object from all sides - look up and you see under the object, look down and you view it from above, look left or right and you see it from the side, then back.
I've tried a number of things like making the object a child of the camera, and using transform.LookAt(target); to keep the camera focused on the object but it isn't working.
New to Unity5 so any help would be very much appreciated.
UPDATE
Using code from a SmoothMouseLook script (http://pastebin.com/vMFkZJAm) this is the closest I've got so far, but it doesn't really work and feels too 'out of control' (the object keeps spinning rather than smoothly turning for inspection) and much less predictable than the 'Exhibit' demo. My guess is that I'm over complicating things. Anyone have any ideas?...
On the Camera(s) ("Main Camera") attach this to keep focused on the object:
using UnityEngine;
using System.Collections;
public class LookAt : MonoBehaviour {
public Transform target;
void Update () {
transform.LookAt(target);
}
}
On the Object, attach this script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SmoothMouseLook : MonoBehaviour
{
/*
This script is used to average the mouse input over x
amount of frames in order to create a smooth mouselook.
*/
//Mouse look sensitivity
public float sensitivityX = 1f;
public float sensitivityY = 1f;
//Default mouse sensitivity
public float defaultSensX = 1f;
public float defaultSensY = 1f;
//Minimum angle you can look up
public float minimumY = -60f;
public float maximumY = 60f;
//Minimum angle you can look up
public float minimumX = -60f;
public float maximumX = 60f;
//Number of frames to be averaged, used for smoothing mouselook
public int frameCounterX = 35;
public int frameCounterY = 35;
//Mouse rotation input
private float rotationX = 0f;
private float rotationY = 0f;
//Used to calculate the rotation of this object
private Quaternion xQuaternion;
private Quaternion yQuaternion;
private Quaternion originalRotation;
//Array of rotations to be averaged
private List<float> rotArrayX = new List<float> ();
private List<float> rotArrayY = new List<float> ();
void Start ()
{
//Lock/Hide cursor
if (GetComponent<Rigidbody>())
GetComponent<Rigidbody>().freezeRotation = true;
originalRotation = transform.localRotation;
}
void FixedUpdate ()
{
//Mouse/Camera Movement Smoothing:
//Average rotationX for smooth mouselook
float rotAverageX = 0f;
//rotationX += Camera.main.transform.eulerAngles.x * sensitivityX;
//rotationX += Cardboard.SDK.HeadRotation.eulerAngles.x * sensitivityX;
rotationX += Cardboard.SDK.HeadPose.Orientation.x * sensitivityX;
rotationX = ClampAngle (rotationX, minimumX, maximumX);
//Add the current rotation to the array, at the last position
rotArrayX.Add (rotationX);
//Reached max number of steps? Remove the oldest rotation from the array
if (rotArrayX.Count >= frameCounterX) {
rotArrayX.RemoveAt (0);
}
//Add all of these rotations together
for (int i_counterX = 0; i_counterX < rotArrayX.Count; i_counterX++) {
//Loop through the array
rotAverageX += rotArrayX[i_counterX];
}
//Now divide by the number of rotations by the number of elements to get the average
rotAverageX /= rotArrayX.Count;
//Average rotationY, same process as above
float rotAverageY = 0;
//rotationY += Camera.main.transform.eulerAngles.y * sensitivityY;
//rotationY += Cardboard.SDK.HeadRotation.eulerAngles.y * sensitivityY;
rotationY += Cardboard.SDK.HeadPose.Orientation.y * sensitivityY;
rotationY = ClampAngle (rotationY, minimumY, maximumY);
rotArrayY.Add (rotationY);
if (rotArrayY.Count >= frameCounterY) {
rotArrayY.RemoveAt (0);
}
for (int i_counterY = 0; i_counterY < rotArrayY.Count; i_counterY++) {
rotAverageY += rotArrayY[i_counterY];
}
rotAverageY /= rotArrayY.Count;
//Apply and rotate this object
xQuaternion = Quaternion.AngleAxis (rotAverageX, Vector3.up);
yQuaternion = Quaternion.AngleAxis (rotAverageY, Vector3.left);
transform.localRotation = originalRotation * xQuaternion * yQuaternion;
}
private float ClampAngle (float angle, float min, float max)
{
if (angle < -360f)
angle += 360f;
if (angle > 360f)
angle -= 360f;
return Mathf.Clamp (angle, min, max);
}
}
For that particular use case, you don't need a script. Assuming you are using the CardboardMain prefab, do this:
Put the object at the origin, and the CardboardMain there too.
In the Cardboard settings, set Neck Model Scale to 0.
Open up CardboardMain and select Main Camera under the Head object.
Set it's Transform Position Z value to a negative value (far enough to see the object).
(You can think of this as the "selfie-stick" camera model.)