I have a player controlled plattform. When moving to the outer edge, the platform starts to move. The width of the outer triggers are calculated by code.
So the player can move to any direction, he just needs to stay near the edge to trigger the movement.
The player got a Rigidbody attached, the platform too. Here is an image of the inspector of platform I use
and this is the code attached
[SerializeField]
private float speed; // the movementSpeed
[SerializeField]
private float movementTriggerWidth; // width of the triggers at the outer edges
private Vector3 movementDirection = Vector3.zero;
private Rigidbody platformRigid;
private GameObject player;
private float triggerDistance; // distance from center to a trigger
private void Start()
{
player = Globals.GetPlayerObject(); // search for the player Object in the scene
platformRigid = GetComponent<Rigidbody>();
triggerDistance = transform.localScale.x / 2 - movementTriggerWidth; // set the trigger distance
}
private void OnTriggerEnter(Collider col)
{
col.transform.parent = transform; // set the player as a child of the platform
}
private void OnTriggerExit(Collider col)
{
col.transform.parent = null; // leave the platform
}
private void OnTriggerStay(Collider col)
{
if (col.gameObject == player) // only the player can move the platform
{
Vector3 playerPosition = player.transform.position;
Vector3 platformPosition = transform.position;
if (Vector3.Distance(playerPosition, platformPosition) > triggerDistance) // player is in outer trigger?
{
movementDirection = playerPosition - platformPosition; // calculate the movement direction
platformRigid.MovePosition(transform.position + movementDirection * speed * Time.deltaTime); // move the platform
}
}
}
Now the problem:
When jumping on a platform, the player becomes a child of the platform. But when the platform starts moving the player is not affected by this. He doesn't get moved by the platform.
I hope someone can help me solving this "little" ( ? ) bug.
Update:
Here is a picture of the player inspector
Well I went for some tries and this is a working solution for me.
I removed the rigidbody
and took this code
[SerializeField]
private float speed;
[SerializeField]
private float movementTriggerWidth;
private Vector3 movementDirection;
private bool move = false;
private GameObject player;
private float triggerDistance;
private void Start()
{
player = Globals.GetPlayerObject();
triggerDistance = transform.localScale.x / 2 - movementTriggerWidth;
}
private void OnTriggerEnter(Collider col)
{
col.transform.parent = transform;
}
private void OnTriggerExit(Collider col)
{
col.transform.parent = null;
}
private void OnTriggerStay(Collider col)
{
if (col.gameObject == player)
{
Vector3 playerPosition = player.transform.position;
Vector3 platformPosition = transform.position;
if (Vector3.Distance(playerPosition, platformPosition) > triggerDistance)
{
movementDirection = playerPosition - platformPosition;
move = true;
}
}
}
private void FixedUpdate()
{
if (move)
transform.Translate(movementDirection * speed * Time.deltaTime);
move = false;
}
Now the platform is moved by Translate() in the FixedUpdate(). OnTriggerStay() just checks, if the platform should be moved or not.
Related
Im throwing a bomb using physics made by code. For some reason it doesnt detect collision unless my physics stop applying force to the object. in order to bypass it I cancelled the applied force and applied gravity to it on collision enter and put the movement of the bomb to LateUpdate so it will trigger after the OnCollisionEnter but the bomb collides only most of the time with the floor (mesh collision, the floor made with ProBuilder) and not all of the time. the bomb collision detection is set to continuous
Will appreciate all the help, Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BombBehavior : MonoBehaviour
{
[SerializeField] float ExplosionForce = 300;
[SerializeField] float ExplosionRadius;
[SerializeField] float LaunchForceX;
[SerializeField] float LaunchForceY;
[SerializeField] float Delay;
float countdown;
Rigidbody rigidbodyy;
float gravity = 1;
Player player;
bool HasExploded = false;
// Start is called before the first frame update
void Start()
{
rigidbodyy = GetComponent<Rigidbody>();
player = Player.p;
GetLaunchForceX();
countdown = Delay;
}
private void GetLaunchForceX()
{
if (transform.position.x > player.transform.position.x)
{
LaunchForceX *= 1;
}
else if (transform.position.x < player.transform.position.x)
{
LaunchForceX *= -1;
}
}
private void LateUpdate()
{
ThrowBomb();
}
private void Update()
{
countdown -= Time.deltaTime;
if (countdown <= 0 && !HasExploded)
{
ExplodeNearEnemy();
}
}
private void ThrowBomb()
{
if (rigidbodyy.useGravity == false)
{
Vector3 ThrowDirection = new Vector3(LaunchForceX, LaunchForceY, 0);
LaunchForceY -= gravity;
ThrowDirection.y = LaunchForceY;
transform.Translate(ThrowDirection * Time.deltaTime);
}
}
private void ExplodeNearEnemy()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, ExplosionRadius);
foreach (Collider collider in colliders)
{
if (collider.gameObject.CompareTag("Enemy"))
{
Rigidbody enemyRB = collider.GetComponent<Rigidbody>();
if (enemyRB != null)
{
enemyRB.useGravity = true;
enemyRB.AddExplosionForce(ExplosionForce, transform.position, ExplosionRadius);
Destroy(enemyRB.gameObject,1);
}
}
}
//Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
LaunchForceY = 0;
LaunchForceX = 0;
gravity = 0;
rigidbodyy.useGravity = true;
}
}
I don't exactly understand the problem but it looks like you want to make a Bomb/Granate so why dont you just write a function and use AddForce()?
void Thow(Vector3 direction, float strength)
{
rigidbodyy.AddForce(direction * strength, ForceMode.Impulse);
}
something like this should help as you only need to get the throwing direction and apply a strength then Unity will handle the rest
Or maybe if your bomb doesn't collide with the ground give the ground a rigidbody and set kinematic to true
Use the MovePosition() to move a rigid body if you want colliders etc to work. Teleporting a rigid body by altering the transform directly messes up the physics.
I am new to Unity and I was working on the enemy's movement of my first video game with C#.
The thing is that whenever the enemy follows the main character, it moves in a very jittery way and I do not know why. I tried a lot of solutions including a different code approach but notthing worked. I tried changing some configurations in its rigid body like collision detection but nothing happend.
This is the code of my enemy:
public class Black : Enemy
{
public float checkRadius;
public float attackRadius;
public bool shouldRotate;
public LayerMask lm;
private Rigidbody2D bd;
private Vector2 movement;
public Vector3 dir;
private bool isInchaseRange;
private bool isInattackRange;
// Start is called before the first frame update
void Start()
{
Debug.Log("Jugador " + player.name);
bd = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
isInchaseRange = Physics2D.OverlapCircle(transform.position, checkRadius, lm);
isInattackRange = Physics2D.OverlapCircle(transform.position, attackRadius, lm);
dir = player.position - transform.position;
dir.Normalize();
movement = dir;
}
void FixedUpdate() {
if (isInchaseRange && !isInattackRange) {
MoveCharacter(movement);
}
if (isInattackRange)
{
bd.velocity = Vector2.zero;
}
}
private void MoveCharacter(Vector2 dir) {
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
}
}
This is how I move my main character if it helps you
public class PlayerMovement : MonoBehaviour
{
public float speed;
private Rigidbody2D rb;
private Vector2 moveAmount;
public float health;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Vector2 moveInput = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
moveAmount = moveInput.normalized * speed;
}
void FixedUpdate()
{
move();
}
void move()
{
rb.MovePosition(rb.position + moveAmount * Time.fixedDeltaTime);
}
public void takeDamage(int damageAmount)
{
health -= damageAmount;
if (health <= 0)
{
Destroy(gameObject);
}
}
}
The character has the same stuff as the enemy: Script, box collider 2d, rigid body
Any ideas on how to solve this?
As you can see by the gif it is not working as it should
https://im2.ezgif.com/tmp/ezgif-2-fbffacda5a27.gif
Thanks
Try making the chase range huge and attack range very small and see if it still moves in a jittery way to make sure it's not jittering because it's going in and out of range very quickly.
Set Rigidbody2D to interpolate from the inspector.
Also for the player, use Time.deltaTime instead of Time.fixedDeltaTime. Although this probably isn't related to your problem, this will give more smooth movement for the player. Time.fixedDeltaTime is just for setting the target you want the unity to stay at, but it will never precisely achieve that frametime. And Time.deltaTime will return the correct value based on whether it's called from Update() or FixedUpdate()
When this is called from inside MonoBehaviour.FixedUpdate, it returns Time.fixedDeltaTime. - Unity Docs
I'm learning Unity, and I'm writing a player script. Based on the script I've written, I expect to see my player to be able to jump while standing still, while moving left, and while moving right. The player cant jump if it is moving right at the same time. I did a bunch of refactoring and reorganizing. I think it might have something to do with Input.GetButton("Jump").
Also, I changed rb2d.AddForce(new Vector2(0.0f, jumpHeight)) to rb2d.velocity = new Vector2(0.0f, jumpHeight), but the player just disappears.
Here's my script so far:
private Rigidbody2D rb2d;
[SerializeField]
private LayerMask whatIsGround;
private bool isTouchingGround;
private bool facingRight = true;
[SerializeField]
private float speed;
[SerializeField]
private float jumpHeight;
[SerializeField]
private Transform[] groundPoints;
[SerializeField]
private float groundRadius;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D>();
}
private void FixedUpdate() {
float moveHorizontal = Input.GetAxis("Horizontal");
Flip(moveHorizontal);
if (Input.GetButton("Jump") && IsGrounded()) {
rb2d.AddForce(new Vector2(0.0f, jumpHeight));
}
rb2d.velocity = new Vector2(moveHorizontal * speed, rb2d.velocity.y);
}
private void Flip(float horizontal) {
if ((horizontal > 0 && !facingRight) || (horizontal < 0 && facingRight)) {
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
private bool IsGrounded() {
if (rb2d.velocity.y <= 0) {
foreach (Transform point in groundPoints) {
Collider2D[] colliders = Physics2D.OverlapCircleAll(point.position, groundRadius, whatIsGround);
foreach (Collider2D collider in colliders) {
if (collider.gameObject != gameObject)
return true;
}
}
}
return false;
}
I found a solution, but I'm not sure why it fixed the problem. If you have an idea why, please comment below!
In the private bool IsGrounded() method, I removed the if statement checking if rb2d.velocity.y <= 0. I thought that would be a helpful check to see if the player is moving down or stationary. For some reason, moving the player right was making this if statement fail. This seems like a bug.
Hey I am making a top down game in unity. The problem I ma having is making npc players change the way they are facing while following the player. So if the player turns left the npc follows them but doesn't turn to face the direction the npc is going. I can get the npc to look like its walking just not change the direction it is looking. This is a 2d top down game please any help will be nice. here is my npc code.
using UnityEngine;
using System.Collections;
public class SlimeController : MonoBehaviour
{
public Transform Character; // Target Object to follow
public float speed = 0.1F; // Enemy speed
public float maxDist = 10.0f;
public float attackdistance = 3;
public float farenough;
private Vector3 directionOfCharacter;
private bool challenged = false;// If the enemy is Challenged to follow by the player
public Transform StartMarker;
private Vector3 goback;
public Transform EndMarker;
public Rigidbody2D rb;
Animator anim;
float oldx;
bool left;
bool right;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim= GetComponent<Animator>();
oldx = transform.position.x;
}
void Update()
{
anim.SetBool("left", false);
anim.SetBool("right", false);
var distanceFromPlayer = Vector3.Distance(Character.position, transform.position);
if(oldx>transform.position.x)
{
left = false;
right = true;
}
if(oldx<transform.position.x)
{
left = true;
right = false;
}
if (oldx == transform.position.x)
{
left = false;
right = false;
}
if (challenged)
{
directionOfCharacter = Character.transform.position - transform.position;
directionOfCharacter = directionOfCharacter.normalized; // Get Direction to Move Towardsss
transform.Translate(directionOfCharacter * speed, Space.World);
enabled = true;
if (distanceFromPlayer < attackdistance)
{
attack();
}
if (distanceFromPlayer > attackdistance)
{
speed = 0.03f;
}
}
if (!challenged)
{
goback = StartMarker.transform.position - transform.position;
goback = goback.normalized;
transform.Translate(goback * speed, Space.World);
}
}
// Will be triggered as soon as player would touch the Enemy Object
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
challenged = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
speed = 0.03f;
challenged = false;
}
}
void attack()
{
speed = 0;
transform.Translate(directionOfCharacter * speed, Space.World);
}
}
This is because you are just moving the object towards your target. But to have it look at your target you need to also rotate it in the direction of your target.
The Transform Component has a function called LookAt. You supply it with your Target and the Axis your object should rotate around. So in your case:
this.transform.LookAt(Character, Vector3.up);
See here for more info on LookAt.
I am messing around in Unity and wanted to make a mechanic where a box would touch another object and then that object would follow the Player.
I have Cube set up like this:
And a Sphere with a Box Collider with same options.
My Player script is thus:
public class Player : MonoBehaviour {
public float speed = 0.0f;
public float moveX = 0.0f;
public float moveY = 0.0f;
public GameObject player;
public GameObject obj;
//public float force = 0.0f;
private bool collided = false;
// Use this for initialization
void Start () {
player = GameObject.FindWithTag ("Player");
}
// Update is called once per frame
void FixedUpdate () {
moveX = Input.GetAxis ("Horizontal");
moveY = Input.GetAxis ("Vertical");
player.GetComponent<Rigidbody> ().velocity = new Vector2 (moveX * speed, moveY * speed);
}
void OnCollisionEnter (Collision col) {
if (col.gameObject == obj) {
collided = true;
}
}
void OnCollisionExit (Collision col) {
if (col.gameObject == obj) {
collided = false;
}
}
void Update () {
if(collided) {
obj.transform.position = (player.transform.position - obj.transform.position)*speed;
}
}
}
What have I yet to do? Hoping someone can nudge me in the right direction.
I will provide you two scripts.
1st Script is FollowTarget. This will follow your target forcely.
2nd Script is SmoothFollow which will follow your target in a smooth movement.
FollowTarget.cs
using System;
using UnityEngine;
public class FollowTarget : MonoBehaviour
{
public Transform target;
public Vector3 offset = new Vector3(0f, 7.5f, 0f);
private void LateUpdate()
{
transform.position = target.position + offset;
}
}
SmoothFollow.cs
using UnityEngine;
public class SmoothFollow : MonoBehaviour
{
// The target we are following
[SerializeField]
private Transform target;
// The distance in the x-z plane to the target
[SerializeField]
private float distance = 10.0f;
// the height we want the camera to be above the target
[SerializeField]
private float height = 5.0f;
[SerializeField]
private float rotationDamping;
[SerializeField]
private float heightDamping;
// Use this for initialization
void Start() { }
// Update is called once per frame
void LateUpdate()
{
// Early out if we don't have a target
if (!target)
return;
// Calculate the current rotation angles
var wantedRotationAngle = target.eulerAngles.y;
var wantedHeight = target.position.y + height;
var currentRotationAngle = transform.eulerAngles.y;
var currentHeight = transform.position.y;
// Damp the rotation around the y-axis
currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime);
// Damp the height
currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime);
// Convert the angle into a rotation
var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0);
// Set the position of the camera on the x-z plane to:
// distance meters behind the target
transform.position = target.position;
transform.position -= currentRotation * Vector3.forward * distance;
// Set the height of the camera
transform.position = new Vector3(transform.position.x ,currentHeight , transform.position.z);
// Always look at the target
transform.LookAt(target);
}
}
Just choose one of them and then attach it to the gameObject. Like another box that is suppose to follow.
Remove your Update() function in your script as you don't need it anymore.
Also Remove your OnCollisionExit()
void OnCollisionEnter (Collision col) {
if (col.gameObject == obj) {
// If you choose to use SmoothFollow Uncomment this.
//col.GetComponent<SmoothFollow>().target = this.transform;
// If you choose to use FollowTarget Uncomment this
//col.GetComponent<FollowTarget>().target = this.transform;
}
}