Creating a buoyant boat in Unity - c#

I need to find a way to make a 3D boat, appear to be buoyant in the water. Currently I am using a boat with a flat bottom and making it slide along the terrain, which is just below the water. This is giving the illusion of buoyancy, but not really what I'm looking for.
The boat moves using
this.transform.Translate(Vector3.left * Time.smoothDeltaTime * ((speed) + 1));
The boat turns using
this.transform.Rotate(Vector3.forward*Time.smoothDeltaTime*(int)(30*horizontal));
The boat has a RigidBody that uses gravity, does not interpolate, and has a Continuous Dynamic collision Detection. It also uses a convex Mesh Collider, with Smooth Sphere Collisions.
The Water has a Box Collider that is used as a trigger.
Now I need a way to make the boat seem to float in the water programmatically.
That means it
Wobbles when it hits something
Doesn't touch the bottom of the "river"
Corrects its rotation to stay flat on the river (doesn't stay crooked after hitting an island or other obstacle)
I would like to do this so that I can give the base of my boat the correct shape so that it can have more realistic collisions with underwater obstacles.

Additional to my comment, if really you want it to be buoyant, you have to make it non-kinematic and react to gravity and the water has to bee a volumetric mesh (or a box as you use, but that's less accurate, do not work with waves if you use water with wave effect).
Basically you'd want to add (at least) 4 objects to your boat and place rigidbody inside it and a script which will apply an upward force which is greater than your gravity.
Then in your OnCollisionStay method set a bool. Maybe something like this (on top of my head)
using UnityEngine;
using System.Collections;
public class Buoyancy : MonoBehaviour {
public float UpwardForce = 12.72f; // 9.81 is the opposite of the default gravity, which is 9.81. If we want the boat not to behave like a submarine the upward force has to be higher than the gravity in order to push the boat to the surface
private bool isInWater = false;
void OnTriggerEnter(Collider collidier) {
isInWater = true;
rigidbody.drag = 5f;
}
void OnTriggerExit(Collider collidier) {
isInWater = false;
rigidbody.drag = 0.05f;
}
void FixedUpdate() {
if(isInWater) {
// apply upward force
Vector3 force = transform.up * UpwardForce;
this.rigidbody.AddRelativeForce(force, ForceMode.Acceleration);
Debug.Log("Upward force: " + force+" #"+Time.time);
}
}
}
And place this on all 4 buoyancy objects (together with a collider or trigger of course). When the object is in water, it will push the boat upwards, if it's over the water, it will be pulled down by the gravity until it reaches water again where it will be pulled up again until it finds a balance.
P.S. If you want to move the boat, you will use the this.rigidbody.AddForce(Vector.forward * 5, ForceMode.Force) (or ForceMode.Accelerate) to move the boat

Use the Buoyancy script. Just add this to the boat, and add the water level (its y value) in the code. Also it is best to use addForce instead of translate to move the boat.
If you want to make the boat react more realistically, you should also put its center of gravity lower, this way it will "wobble" and it stays right side up.

Related

2d Ball character controller

I wish to create a ball character similar to red ball game in unity 2d but I can't seem to get it to work like the one in red ball. I wish for the ball to ROLL left and right and be able to Jump. I managed to make it roll left and right by adding a physics material and bumping up the friction and adding the rb.AddForce() function but I am having trouble with the jumping. I tried rb.velocity() but when I jump and move right or left, the ball adds force too strongly and it just moves too swiftly . Am I missing something or is there a better way of doing this? I need help.....
A quick fix for this would be to define an upper limit. Like:
float limit = 10f;
Rigidbody2D rig;
void Start(){
rig = gameObject.transform.GetComponent<Rigidbody2D>();
}
void Update(){
if(Input.GetKeyDown(KeyCode.A && rig.velocity.magnitude < limit){
rig.AddForce(accelerationVariable);
}
}
I would use Rigidbody.velocity.magnitude because it gives you the length of the vector.
If you just want to check the x-Force use Rigidbody.velocity.x
Hope that helps

Rotating 2D world within Unity

I am looking at developing a game within Unity by using C#
The game will take the gyroscope orientation and rotate accordingly to the direction of which the phone is rotated.
I am curious of how this would work, I understand how I would be able to read and update the gyroscope orientation however I am unsure on how to assign this to a world to rotate. There will be a player on world which will be the next challenge to prevent the player clipping through the world when it is rotated.
Hence the world should rotate around the players current location.
I currently have no code as I am in the process of designing this, however i am unable to get the logic of how to make this work within my head
Thankyou
If I get your objective right, I guess rotating the camera like in a Fist-Person game should be enough, and would be way simpler than rotating the whole world while keeping the player static.
This is how I would go about it, first I wouldn't rotate the world, as that is alot of objects to rotate, but if you really wanted to you could parent all of the world to a single game object, then rotate that object about an axis based off of the players position(See this: https://docs.unity3d.com/ScriptReference/Transform.RotateAround.html).
The simplest way is to attach your main camera to your player(Drag and drop it on your Player Object in your hierchy), then rotate your player.
for an example you can test on your machine without the gyroscrope:
using UnityEngine;
public class rotatorScript: MonoBehaviour {
void Update() {
// Will Rotate based off of left/right arrows
float rotator = -Input.GetAxisRaw("Horizontal");
// Move up or down based off of up/down Arrows
float verticalDirection = Input.GetAxisRaw("Vertical");
// Do the actual movement... using space.self so it is based on the player not the world.
transform.Translate(Vector3.up * verticalDirection * Time.deltaTime * 5f, Space.self);
transform.Rotate(0f,0f,90f * Time.deltaTime * rotator);
}
}
This script would be attached to the player.

What part of the turret should I rotate to face the target ? And at what speed?

The code that rotate the turret is kind simple:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotateturret : MonoBehaviour
{
public Transform target;
public Transform partToRotate;
public float turnSpeed = 10f;
private void Update()
{
transform.LookAt(target);
}
}
The target in this case is rotatingaround the turret in fixed speed but changing the height randomly.
If this script RotateTurret if I attach it to the Turret child part it will rotate and will look at the target. Like doing LookAt.
This is a screenshot of the Turret part:
And if I attach the script to the Pylon part it will rotate slowly and will always stay behind the target it will not rotate fast enough.
Screenshot of the Pylon part:
My problems are: What part of the turret should I rotate ? The logic say the Pylon I think.
Second is how should I calculate the speed the turret should rotate ? Does it always should facing perfect to the target ? Or sometimes if the target is moving in random speeds the turret will not facing it all the time 100% ? What is the logic in this case ? Just using LookAt is not enough. And I'm not using physics yet that's another problem I guess. If both turret and target will use physics but for now not sure how to do the turret logic (Or should I call it AI).
For testing I tried this script too:
public class Rotateturret : MonoBehaviour
{
public Transform target;
public Transform partToRotate;
public float turnSpeed = 10f;
private void Update()
{
Debug.DrawRay(transform.position, transform.forward, Color.green, 1000);
Vector3 dir = target.position - transform.position;
Quaternion lookRotation = Quaternion.LookRotation(dir);
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles;
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f);
}
}
And again if I will rotate the Turret it will be more accurate but if the pylon it will be behind the target. And who said the speed of the rotating of the turret should be 10 and 20 or 50 or 1 ? There is no much logic in this script either I guess.
Another thing is that in the future I will want also to rotate the Gun to shoot bullets. So the rate fire and when to start firing that's another problem maybe that will be another question, But I guess the firing shooting bullets is also relevant to the turret rotating logic.
Firstly, note that from your screenshot, the Pylon gameobject's forward direction (blue arrow) is perpendicular to the turret barrel's direction. Thus, attaching your script on that gameobject will not produce your desired behaviour.
When it comes to the implementation of a turret's movement, it depends greatly on the context of your game.
For example, if the turret is to be used in a Tower Defence game where it should never miss, then typically the turret's rotation and shooting animation should just be an effect. Your actual damage dealing to the target will be done via script directly (e.g searching for viable target in range and sending an onShoot event). In this case, a transform.lookAt() is good enough, as it produces a reasonable visual effect.
However, if your turret is to be used in a First Player Shooter game, expect then that it's rotation will be controlled by the player (either via transform or rigidbody physics). Whether or not the turret should deal damage to the enemy depends on whether the projectile (simulated via physics with rigidbody and collider) hits the enemy or not. In this case, you have to limit your turret's maximum rotation speed. How much the threshold is depends on how realistic you want it to be.
Firstly, there is a LookRotation overload that takes the axis as a second parameter, so if you try
Quaternion lookRotation = Quaternion.LookRotation(dir,Vector3.up);
the rotation will always be around a vertical axis. This way you can split up motion into seperate axis as you would with a mechanical device (i.e. the pylon only rotates around a vertical axis)
As for the second part, i.e. how to make the turret hit the target, there's several approaches you can take:
You could, for example take the distance between the turret and the target, divide bullet velocity by target velocity, and take target.transform.forward multiplied by that ratio and the distance (this is from head, but you get the idea).
A bit more interesting alternative is to take the opportunity to learn about PID controllers, turrets are a fantastic example of how much can you do with a very simple controller.
PID stands for Proportional, Integral and Deriviative, and you compute your actuator output (i.e. force that drives your rotation) based on those three ways of looking at values.
In the most simplitied form it works like that: Proportional is your direct delta - how much of an angle away from your target angle you are (or from the target forward point). Than you average out that parameter (i.e. lerp the current value towards the target or using moving average) by a signifficant amount, this is your integral part - it will follow the target in a much slower way. Now if you subtract your averaged (integrated) value from your current (propoprtional) value, you get something that represents your change, this can be used as a deiviative component. Now this last part is what makes the turrent react to when the target is changing trajectory, you can make the output overshoot the change, or tune it in multiple other ways

Creating an endless spinner in Unity

I want a gameobject spinning around its y-axis. This spinner should have a initial movement direction and when colliding with something, it should change its direction.
I created a little picture to show what I mean, but I want the behaviour for a 3D game.
So I just started with the rotation of the spinner,
public class Spinner : MonoBehaviour
{
[SerializeField]
private float movementSpeed; // speed when moving
[SerializeField]
private float rotationSpeed; // speed when rotating
private Rigidbody rigid;
private Vector3 movementDirection; // the direction the spinner is moving
private void Start()
{
rigid = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
transform.Rotate(0, rotationSpeed, 0); // rotate the spinner around its y-axis
}
private void OnCollisionEnter(Collision col)
{
// set new direction
}
}
How can I move the spinner, that it moves along a direction and whenever it collides with something, it changes its direction. It should never stop moving or rotating.
I would like to point out a few things:
The Unity Physics Engine will make collisions to absorb part of the force which moves your spinner, so unless you keep adding "artificial" forces to the spinner it will eventually stop.
The "air friction" in your scene will also reduce the force of your
spinner, so it will slow it down. You should add a material to the
spinner which has 0 Dynamic Friction
Based on the comment you left in #reymoss' answer, you may consider
to add the bouncy material to the walls, and not to the spinning
GameObject.
To sum up, the issue here is if you want a GameObject to bounce against a wall using the Physics Engine, the object will eventually stop, because some of the forces will be absorbed in each collision. That means you will need to keep adding external forces every time a collision takes place, to keep it moving endlessly.
Something you can try is,
1- Add a bouncy material to the walls and remove the dynamic friction of your spinner. In the following link you can learn about this:
https://docs.unity3d.com/Manual/class-PhysicMaterial.html
2- Add to the Spinner a Collider, so when it detects a collision with a wall (you can tag the walls as so for example) add an additional force to the spinner in the direction it is already moving, so it will compensate the energy lost during the collision.
void OnTriggerExit(Collider other) {
if(other.tag == "wall") {
rigidbody.AddForce(rigidbody.velocity.normalized * Time.deltaTime * forceAmount);
}
}
The idea is to let the Engine decide the direction of the Spinner after the collision with the wall, and as soon as the GameObject leaves the trigger, you add an additional force in the current direction.
Then you can play with the Bounciness of the wall's material and with the forceAmount, until your spinner moves as you have in mind.
Note: Since you will have 2 materials, one in the walls and another in the spinner, maybe playing with the Friction Combine and Bounce Combine you will be able to avoid the force lost during collisions, so you will not need to add the external force I mention in the second step. However I have never tried this.
Let me know if it works. I can't try myself the solution until I arrive home.
If you give the object an initial velocity, attach a collider, create and assign a Physics Material 2D to the collider (to apply bounciness), and attach Colliders to the walls, you can have it bounce around with minimal code.
private void Start()
{
rigid = GetComponent<Rigidbody>();
rigid.velocity = new Vector3(2f, 3f, 1f) // initialize velocity here
}

Position and rotation of point in relation to two other points

To give you the setting: I'm making a Vive game in zero-g, where the player moves by grabbing handles and propelling themselves.
What I'd like is for the player to be able to rotate themselves, by grabbing a handle with both hands. Imagine how you'd move in zero-g if you held on to a bar with both hands.
To illustrate:
On the left hand side the player has grabbed a handlebar with both hands. Left arm extended, right arm bent.
In the right hand side picture the player has now extended their right arm, which has rotated the player around the bar.
I guess it's easier to see it as if the player would be moving the entire world, when they do this.
My question is: How can I do this in Unity in 3 dimensions, either through math of Unity-trickery? It needs to roll, yaw and position the player relative to the hands.
Thank you!
Record the average of the three vectors. Then in the next frame, get the difference from the previous average. The difference can be used as euler angles to apply constant force to a rigidbody (or rotate an object by that amount, or other possibilities, depending on your goals).
https://docs.unity3d.com/ScriptReference/Transform.Rotate.html
https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html
Vector3 previousCenterPoint;
void Update() {
Vector3 newCenterPoint = (leftHand.transform.position + rightHand.transform.position + player.transform.position) / 3f;
if (previousCenterPoint != null)
{
Vector3 someEulerAngles = newCenterPoint - previousCenterPoint;
someRigidBody.AddForce(someEulerAngles, ForceMode.VelocityChange);
}
previousCenterPoint = newCenterPoint;
}

Categories

Resources