I've been trying to figure this out for a few days now, and I'm very sure it has something to do with my code. What I want the enemy monster to do is to stop moving forward when the player right next to it and run the attack animation. What it actually does is run the attack animation while orbiting around the player like a planet, and you can't really outrun it.
I'm using Unity3d as the engine and my code is written in C#. It's very simple so I'm worried I'm missing something important.
public class enemyAITest : MonoBehaviour
{
public Transform player; // target
public float playerDistance; // determines how far from target before action takes place
public float rotationDamping; // determines how quickly rotation occurs
public float chaseSpeed; // determines how quickly to pursue target
public float wanderSpeed; // determines how quickly to move around map
public float maxDist;
static Animator anim;
Vector3 wayPoint;
void Awake()
{
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update ()
{
transform.position += transform.TransformDirection(Vector3.forward) * wanderSpeed * Time.deltaTime;
playerDistance = Vector3.Distance(player.position, transform.position);
while ((transform.position - wayPoint).magnitude < 3)
Wander();
if (playerDistance < 20f)
{
LookAtPlayer();
wanderSpeed = 0;
anim.SetBool("isIdle", true);
}
if (playerDistance < 10f)
{
Chase();
anim.SetBool("isIdle", false);
anim.SetBool("isWalking", false);
anim.SetBool("isRunning", true);
}
if(playerDistance < 1f)
{
wanderSpeed = 0;
anim.SetBool("isRunning", false);
anim.SetBool("isAttacking", true);
}
else anim.SetBool("isWalking", true);
}
void LookAtPlayer()
{
Quaternion rotation = Quaternion.LookRotation(player.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotationDamping);
wanderSpeed = 0;
}
void Chase()
{
Vector3 direction = player.position - transform.position;
direction.y = 0;
transform.Translate(Vector3.forward * chaseSpeed * Time.deltaTime);
}
void Wander()
{
Vector3 MonsterPosition = new Vector3(Random.Range(transform.position.x - maxDist, transform.position.x + maxDist), 1, Random.Range(transform.position.z - maxDist, transform.position.z + maxDist));
wayPoint.y = 1;
transform.LookAt(wayPoint);
//Debug.Log(wayPoint + " and " + (transform.position - wayPoint).magnitude);
}
}
Any advice will help (Please forgive the strange formatting, I was having trouble with keeping the entire Wander method and the on Awake line in the code block).
Related
I just can’t figure out how to make random points for patrolling, I went through 100 manuals, but I can’t do it.
No need to write about NavMeshAgent. Not used.
using System.Collections;
using UnityEngine;
public sealed class Manikin : MonoBehaviour {
private Vector3 StartManikin = Vector3.zero,
NewPosition = Vector3.zero;
private readonly float Speed = 2.0f;
void Start() {
StartManikin = transform.position;
}
void Update() {
if (Vector3.zero == NewPosition) {
NewPosition = StartManikin + Random.onUnitSphere * 2;
}
if (Vector3.Distance(transform.position, NewPosition) < 0.1f) {
StartManikin = transform.position;
NewPosition = Vector3.zero;
} else {
transform.LookAt(new Vector3(NewPosition.x, StartManikin.y, NewPosition.z));
transform.position = Vector3.MoveTowards(transform.position, NewPosition, Speed * Time.deltaTime);
}
}
}
The problem is that there may be obstacles in the form of a fence, a tree, houses, cars, etc.
I need that when the enemy appears, random points are generated in his radius so that he does not leave further.
Help me out, I can’t figure out what needs to be done to make everything work ...
It turns out that I just did not see that the Pivot of the model is not clear where, it flies in another dimension, the problem is solved, it is necessary to reset all transformations and the world point of the model's coordinates.
I didn’t see it right away, all the options turned out to be working ...
And also, it was necessary to fix the problem with the take-off along the y-axis, so that this does not happen, you need to look at the bottom point of the object from which we will spawn the coordinate for it.
if (Vector3.zero == NewPosition) {
NewPosition = StartManikin + (Random.onUnitSphere * 5);
NewPosition.y = transform.GetComponent<CapsuleCollider>().bounds.min.y;
}
if (Vector3.Distance(transform.position, NewPosition) < 0.1f) {
StartManikin = transform.position;
NewPosition = Vector3.zero;
} else {
transform.LookAt(new Vector3(NewPosition.x, transform.position.y, NewPosition.z));
transform.position = Vector3.MoveTowards(transform.position, NewPosition, Speed * Time.deltaTime);
}
I have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}
I'm trying to setup a simple "ai" system that follows the waypoint and after the last waypoint I want it to go the first waypoint. Here's what I got so far:
// put the points from unity interface
public Transform[] wayPointList;
public int currentWayPoint = 0;
Transform targetWayPoint;
public float speed = 4f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
// check if we have somewere to walk
if(currentWayPoint < this.wayPointList.Length)
{
if(targetWayPoint == null)
targetWayPoint = wayPointList[currentWayPoint];
walk();
}
}
void walk(){
// rotate towards the target
transform.forward = Vector3.RotateTowards(transform.forward, targetWayPoint.position - transform.position, speed*Time.deltaTime, 0.0f);
// move towards the target
transform.position = Vector3.MoveTowards(transform.position, targetWayPoint.position, speed*Time.deltaTime);
if(transform.position == targetWayPoint.position)
{
currentWayPoint ++ ;
targetWayPoint = wayPointList[currentWayPoint];
}
}
Seems like you need to reset when fully path is complete?
try this:
void walk()
{
// rotate towards the target
transform.forward = Vector3.RotateTowards(transform.forward, targetWayPoint.position - transform.position, speed * Time.deltaTime, 0.0f);
// move towards the target
transform.position = Vector3.MoveTowards(transform.position, targetWayPoint.position, speed * Time.deltaTime);
if (transform.position == targetWayPoint.position)
{
if (currentWayPoint == this.wayPointList.Length-1)
currentWayPoint = 0;
else
currentWayPoint++;
targetWayPoint = wayPointList[currentWayPoint];
}
}
Just do currentWaypoint = (currentWaypoint + 1) % totalWaypoints;
The base is a red cube.
The spaceship is moving already when the game start.
When I click/press the L button the spaceship rotates to face the base and starts moving to it but then when it's getting close to the base it's behaving unexpectedly and the spaceship starts rolling around the base nonstop.
What I want is to make the landing automatic like this youtube video of blender.
I don't want the graphics but the way it's landing.
Blender landing spaceship
And this is a short video clip showing my spaceship when it's start landing:
Landing test
This is the script i'm using for controlling the spaceship and the landing part should be automatic.
The script is attached to the spaceship.
using UnityEngine;
using System.Collections;
public class ControlShip : MonoBehaviour {
public int rotationSpeed = 75;
public int movementspeed = 10;
public int thrust = 10;
public float RotationSpeed = 5;
private bool isPKeyDown = false;
private float acceleration = .0f;
private Vector3 previousPosition = Vector3.zero;
private Rigidbody _rigidbody;
private bool landing = false;
private Vector3 originPosition;
private Vector3 lastPosition;
private const float minDistance = 0.2f;
private Transform baseTarget;
// Use this for initialization
void Start () {
baseTarget = GameObject.Find("Base").transform;
originPosition = transform.position;
_rigidbody = GetComponent<Rigidbody>();
Debug.Log("Acc Speed: " + thrust);
}
// Update is called once per frame
void Update()
{
if (landing == false)
{
var v3 = new Vector3(Input.GetAxis("Vertical"), Input.GetAxis("Horizontal"), 0.0f);
transform.Rotate(v3 * rotationSpeed * Time.deltaTime);
transform.position += transform.forward * Time.deltaTime * movementspeed;
if (Input.GetKey(KeyCode.Z))
transform.Rotate(Vector3.forward * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.R))
transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.P))
{
isPKeyDown = Input.GetKey(KeyCode.P);
float distance = Vector3.Distance(previousPosition, transform.position);
acceleration = distance / Mathf.Pow(Time.deltaTime, 2);
previousPosition = transform.position;
_rigidbody.AddRelativeForce(0f, 0f, thrust, ForceMode.Acceleration);
}
}
else
{
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
}
if (landed == true)
TakeOff();
if (Input.GetKey(KeyCode.L))
{
landing = true;
lastPosition = transform.position;
}
}
void OnTriggerEnter(Collider other)
{
if (landing == true && other.gameObject.name == "Base")
{
StartCoroutine(Landed());
}
}
bool landed = false;
IEnumerator Landed()
{
yield return new WaitForSeconds(5);
Debug.Log("Landed");
landed = true;
}
private void TakeOff()
{
if (transform.position != originPosition)
{
_rigidbody.AddForce(transform.up * 10);
}
if ((transform.position - originPosition).sqrMagnitude <= (1f * 1f))
{
landed = false;
_rigidbody.useGravity = false;
}
}
void OnGUI()
{
if (isPKeyDown)
{
GUI.Label(new Rect(100, 100, 200, 200), "Acc Speed: " + acceleration);
}
}
}
This is the part of the landing, should be the part of the landing:
transform.position += transform.forward * Time.deltaTime * movementspeed;
var targetRotation = Quaternion.LookRotation(baseTarget.position - transform.position);
var str = Mathf.Min(.5f * Time.deltaTime, 1);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, str);
The spaceship have two components: Rigidbody, Use Gravity set to true. And a Box Collider.
The Base have a box collider component.
You appear to have a box collider on your vehicle and it looks as though it is colliding with the terrain when your code tries to bring it in for a landing. Try switching the collider to be a trigger(tick option on collider component).
Then try it again, as this will not cause physical collisions. If it works or fails for a totally different reason you know this is the cause or a contributing factor.
EDIT: It is worth also noting that when trying to achieve this kind of effect it can be much easier to trigger an animation than try to achieve it in physics based code. Unity offers some great animation controllers and you can call an animation when you need to land since you are taking control away from the player anyway.
While doing this you could turn off the collider so you don't get any strange collision then turn it on when the ship needs to take off, provided you need that of course.
Hope it helps.
I am working on a simple patrol script, and everything works but the characters are randomly rolling through the floor when they turn around. Here is a short clip of them...
Here is my script...
public class Patrol : MonoBehaviour
{
public float speed = 5;
public float directionChangeInterval = 1;
public float maxHeadingChange = 30;
public bool useRootMotion;
CharacterController controller;
float heading;
Vector3 targetRotation;
Vector3 forward
{
get { return transform.TransformDirection(Vector3.forward); }
}
void Awake()
{
controller = GetComponent<CharacterController>();
// Set random initial rotation
heading = Random.Range(0, 360);
transform.eulerAngles = new Vector3(0, heading, 0);
StartCoroutine(NewHeadingRoutine());
}
void Update()
{
transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, targetRotation, Time.deltaTime * directionChangeInterval);
if (useRootMotion)
{
return;
}
else
{
controller.SimpleMove(forward * speed);
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.gameObject.tag == "Player")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
if (hit.gameObject.tag == "Boundary")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
}
/// Finds a new direction to move towards.
void NewHeading()
{
var floor = transform.eulerAngles.y - maxHeadingChange;
var ceil = transform.eulerAngles.y + maxHeadingChange;
heading = Random.Range(floor, ceil);
targetRotation = new Vector3(0, heading, 0);
}
/// Repeatedly calculates a new direction to move towards.
IEnumerator NewHeadingRoutine()
{
while (true)
{
NewHeading();
yield return new WaitForSeconds(directionChangeInterval);
}
}
}
I have tried adding a rigidbody to the characters and constraining rotation, but that doesnt work. Oddly enough, the character control isnt rotating at all. In the scene view I can see the character collider staying as it should, but the character flips through the mesh on its own.
It looks like it's because they are walking into a corner and being bounced between the two walls constantly which causes them to behave strangely. I would add a method of checking for a series of very quick collisions to detect that they are in a corner or stuck and then adapt accordingly, perhaps with a method to rotate 180 degrees and keep walking or the like.
You can do it like this:
float fTime = 0.1f
float fTimer = 0;
int iCollisionCounter;
if(collision){
if(fTimer > 0) iCollisionCounter++;
if(iCollisionCounter >= 5) //Character is stuck
fTimer = fTime;
}
Void Update(){
fTimer -= time.deltaTime;
}
That means that if there are multiple collisions within 0.1 seconds of each other you can handle it.
Hope this helps!