Avoid falling off edge - c#

I'm trying to make a simple FPS game in Unity3d where a character cannot fall off the platform unless they jump off of it, walking off should be impossible.
I made a script for objects that are on a moving platform:
public GameObject Player;
private void OnTriggerEnter(Collider other)
{
if (other.gameObject == Player)
{
Player.transform.parent = transform;
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject == Player)
{
Player.transform.parent = null;
}
}
And the only way I came up with to solve this problem is to create a short, invisible box collider working as a threshold, but adding it to every edge of every walking space would be a nightmare. Also I can't use nav mesh for it.

Raycasting just in front of the player pointing down and seeing if it hits the level would be one option, however it's very expensive to run this a lot.
If your level is square (other shapes will work but will require some maths), you can take note of the top left and bottom right points as your boundary points and check if your players position + walking speed per frame would be outside this boundary square. If it is, deny the move.
E.g
if((transform.position + transform.forward * walkingSpeed * time.deltatime).x > maxX || < minX || .y > maxY || .y << minY)
//Deny the move

In the same way that you use colliders to detect the ground to allow / disallow jumping, you can also use colliders to detect edges of platforms.
Add a small collider to each corner or midpoint of each side of the bottom of your player object. The player can only move in directionA if the colliders on sideA of the player are in contact with the ground. This means that as you approach a ledge, the nearest side to the ledge will lose contact with the ground, therefore preventing any more movement in that direction.
If player is jumping, then allow movement in all directions regardless of collider contact.
You will have to play around with the position of these colliders and which directions you allow movement, but this seems like a simple yet effective approach to me.

Create a prefab for the edges, everywhere there is an edge place the prefab so that the prefab is really the edge. This will ensure that the code that works is always present on an edge.

Related

Instantiate and Fire Object, when OnTriggerEnter or OnCollisionEnter

I would like a bullet that is fired from the Enemy, to bounce back off my Players shield.
I have set up my character and the enemy fires towards me at time intervals.
I can then activate my players shield (turning on a BoxCollider) and pressing a button.
So when the Bullet (IsTrigger) collides with my Players Shield (IsNotTrigger) I want to remove this Bullet and then Instantiate a new Bullet from the shield, in the direction of the Enemy.
I am also having an issue destroying the Bullet.
When the OnTriggerEvent or OnColliderEvent occurs (tried both), hundreds of bullets will appear from my Shield. How do I only allow one bullet to be fired towards the enemy?
Below is part of my script, which is located in the GameObject of my Players Shield.
Ideally I would like to destroy the bullet once it has collided with an object.
void OnTriggerEnter(Collider col) {
if (col.tag == "Weapon") {
attack();
}
}
private void attack() {
if (!GameManager.instance.GameOver) {
bulletReturnClone = Instantiate(bulletReturn, transform.position, transform.rotation) as GameObject;
bulletReturnClone.GetComponent<Rigidbody>().velocity = transform.forward * 25f;
Strachan,
That's not how I would approach the problem but I will stick to your desired solution then share some ideas for improvement.
void OnTriggerEnter(Collider col) {
if (col.tag == "Weapon") {
attack(col);
}
}
private void attack(Collider col) {
if (!GameManager.instance.GameOver) {
bulletReturnClone = Instantiate(bulletReturn, transform.position, transform.rotation) as GameObject;
bulletReturnClone.GetComponent<Rigidbody>().velocity = transform.forward * 25f;
// the following line of code should remove the bullet from the shield collider in order to prevent any future problems like spawning multiple bullets or instantly destroying the newly created bullet
bulletReturnClone.GetComponent<Transform>().position *= bulletReturnClone.GetComponent<Rigidbody>().velocity;
Destroy(col.gameObject);
}
}
If your trigger(Bullet) is tagged as weapon this code should achieve your intentional goal of reflecting the bullet in the direction of your shield pointing to by destroying the bullet and instantiating a new one and modifying it's velocity. It works but it's kind of sloppy development. It can be much better if you approach the problem from a different perspective - the one of the Bullet not the Shield.
Let's pretend for a moment you are a Bullet. All you do is fly in the direction you are shot. Once you collide with a terrain you stop/disappear. If this terrain is a shield you don't stop/disappear but change your direction (you get reflected).
So... long story short... the one who should have a trigger collider is the bullet not the shield. OnTriggerEnter(Collider col) for the bullet script will destroy it but if col.tag == "Shield" the bullet will only change its direction without all the useless instantiations and transformations.
I'm too lazy to write the code for the 2nd solution. If you got my point you should be able to easily write it down. Also learning through trials and errors helps you develop (excuse for me being lazy).
Im not really sure why you would want to despawn and respawn the bullet with a new velocity?
Depending on the shield geometry you could look up Coefficients of Restitution and therefore reflect the kinetic energy of the bullet into a realistic velocity.
Note the complexity of that maths will be proportional to the complexity of your shield geometry depending on the different collision primitives.
Sphere-sphere
Sphere-plane
Sphere-terrain (terrain could represent any un-even surface)
Or are you trying to collect the bullets in some sort of "charge" mechanic to release them back at a different time?

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;
}

Detecting collision for 2 colliders using physics2d

I have an object with circle collider 2d, and an object with box collider 2d. how do i detect when the circlecollider hits the top edge of the box collider. assuming none objects have any rotation. i want to do this in code C#
OnCollisionEnter triggers when something collides with the object containing this script, used like this:
void OnCollisionEnter(Collider collider)
{
foreach(CollisionPoint contact in collider.contacts)
{
//Do something
}
}
The above will give you a list of contact points for the collision, via which you should be able to determine where the collision occurred. An alternative method if you only need to detect collisions at a specified location, you could place an invisible child object within your cube, with a collider at the spot you want.
Edit:
Since you mentioned raycasts, there's 2 ways I can think of that these could be implemented. The first is to fire it upwards from the cube, but this has the issue of the ray firing from just 1 point, meaning some collisions might be missed (depending on the size of the cube & sphere).
The second method that pops to mind is to fire the ray parallel to the cube. This might sound a bit unorthodox, and I haven't tested it, but in theory it should work. Stick it in your cube:
void Update
{
Vector3 start = this.transform.position;
Vector3 end= this.transform.position;
//This attempts to place the start & end point just above the cube
//This of course assumes the cube isn't rolling around. If that's the case
//then these calculations get quite a bit more complicated
//Additionally the 0.01 might need adjusting if it's too high up off the cube
start.y += this.renderer.bounds.y/2 + 0.01f;
end.y += this.renderer.bounds.y/2 + 0.01f;
start.x -= this.renderer.bounds.x/2;
end.x += this.renderer.bounds.x/2;
Ray ray = new Ray(start, end);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, start.x-end.x) && hit.name == "mySphere")
{
//Theoretically, the sphere hit the top of the box!
}
}

Creating a buoyant boat in Unity

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.

Dealing with Projectile Misses and trajectory

I have several formulas I use for firing a bullet at a target, but it all breaks down when the player moves from his original position and the bullet reaches the old position of the player as intended.
When PlayerPosition == BulletPosition, how do I make the bullet keep going in the right direction if it misses? My problem is once the bullet reaches where it was supposed to go it stops and I need a new formula to keep it moving.
If it hits the player, that's easy, remove the item, but I can't seem to find a good solution. Below is some code, it's super simple for now.
var movement = PlayerPosition - Position;
if (movement != Vector2.Zero)
movement.Normalize();
//var angle = Math.Atan2(movement.Y, movement.X);
Position += movement*_projectileMoveSpeed;
Did you intend the bullet's speed to be affected by the distance from the player?
I'd just save the velocity, then use that in the future frames. In pseudocode:
to shoot a bullet:
direction is sign(PlayerPosition - Position)
in each frame:
for each bullet:
modify position by direction * projectileMoveSpeed
handle collision (player or screen edge)

Categories

Resources