Reputation: 3
What I need is that an NPC that is moving in a path by script, stops in front of the player if the player is in the way. It's a top-down 2d game rpg with grid movement. The NPC is moving 4 cells down, 2 cells left, 4 cells up and 2 cells right all the time. I need that if the player is in the way, it stops in front and continue when the player leaves the grid. I don't want to use a collider on the player because then I need a rigitbody2d and with that, my movement script is not working.
Here is the TileMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class TileMovementController : MonoBehaviour {
public enum Direction { Left, Right, Up, Down }; // Direction of movement
protected Vector3 newPosition; // For movement
protected virtual Vector3 move(Direction dir, int steps) {
if (dir == Direction.Left && transform.position == newPosition)
newPosition += new Vector3(steps * (-1), 0, 0);
else if (dir == Direction.Right && transform.position == newPosition)
newPosition += new Vector3(steps, 0, 0);
else if (dir == Direction.Up && transform.position == newPosition)
newPosition += new Vector3(0, steps, 0);
else if (dir == Direction.Down && transform.position == newPosition)
newPosition += new Vector3(0, steps * (-1), 0);
return newPosition;
}
}
The PlayerMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementController : TileMovementController {
public float moveSpeed = 2.0f;
void Start () {
newPosition = transform.position; // Take the initial position
}
private void FixedUpdate() {
// RayCasts for collisions, ignoring layer 2 (Ignore Raycast)
if (Input.GetKey(KeyCode.A) && Physics2D.Raycast(transform.position, Vector2.left, 1, ~(1 << 2)).collider == null) { // Left
newPosition = move(Direction.Left, 1);
}
if (Input.GetKey(KeyCode.D) && Physics2D.Raycast(transform.position, Vector2.right, 1, ~(1 << 2)).collider == null) { // Right
newPosition = move(Direction.Right, 1);
}
if (Input.GetKey(KeyCode.W) && Physics2D.Raycast(transform.position, Vector2.up, 1, ~(1 << 2)).collider == null) { // Up
newPosition = move(Direction.Up, 1);
}
if (Input.GetKey(KeyCode.S) && Physics2D.Raycast(transform.position, Vector2.down, 1, ~(1 << 2)).collider == null) { // Down
newPosition = move(Direction.Down, 1);
}
transform.position = Vector3.MoveTowards(transform.position, newPosition, Time.deltaTime * moveSpeed); // Move there
}
}
And the NpcMovementController.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NpcMovementController : TileMovementController {
// Defines the direction and number of steps in that direction has to move
[System.Serializable]
public struct MoveStep {
public Direction direction;
public int steps; // Each step is a grid square. 1 step = 1 unit in the grid position
public MoveStep(Direction direction, int steps) {
this.direction = direction;
this.steps = steps;
}
}
public List<MoveStep> path = new List<MoveStep>();
public float moveSpeed = 2.0f;
private int nextStepIndex = 0;
private bool waiting = false;
void Start () {
newPosition = transform.position; // Take the initial position
}
private void FixedUpdate() {
if (path.Count > 0) {
if (Vector3.Distance(transform.position, newPosition) < 0.05f && !waiting) {
transform.position = newPosition; // Adjust the position to be exactly what it should be
waiting = true;
StartCoroutine("wait", 2f);
} else {
if (Vector3.Distance(transform.position, newPosition) > 0.05f)
transform.position = Vector3.MoveTowards(transform.position, newPosition, Time.deltaTime * moveSpeed);
}
}
}
IEnumerator wait(float seconds) {
yield return new WaitForSecondsRealtime(seconds);
newPosition = move(path[nextStepIndex].direction, path[nextStepIndex].steps);
if (nextStepIndex == path.Count - 1)
nextStepIndex = 0;
else
nextStepIndex++;
waiting = false;
}
}
In the NPCMovementController I tried to do a raycast to player, but without the collider/rigitbody it does not work obviously. If I attach the collider/rigitbody it detects the player but I cannot move.
Upvotes: 0
Views: 949
Reputation: 7605
I did once something similar and I solved by checking if the position of the player is inside the grid squares which represent the path of the npc. For example.
Let's imagine that the npc moves like this:
(0,0) -> (0,1) -> (0,2) -> (0,3) -> (1,3) -> (2,3) -> (2,2) -> (2,1) -> (2,0) -> (1,0) -> (0,0) loop
So for my understanding, if the npc is in (0,0), it will detect the player in (0,1), (0,2) and (0,3). So you can check this three positions in your grid and compare them with the current position of the player, if there is a match, the player is in the path.
Now besides that, it is possible to move your player using the rigidbody (so you can keep with your raycast approach). You can use the velocity of the rigidbody to move the npc in one direction or another.
//moveHorizontal and moveVertical can have +1 or -1 values depending on the inputs of the player:
Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
rigidbody.velocity = movement * speed;
Upvotes: 1