I am trying to build a game where the AI needs to keep running away from the player and keep collecting randomly spawning collectables on the map. How do I make it rotate towards a newly spawned collectible object?
Both player and opponent need to move peculiarly, as shown in the code below..
if (touch.position.x < Screen.width / 2 && touch.position.y < Screen.height / 2) {
Player.Rotate (0, 0, 5);
}
if (touch.position.x > Screen.width / 2 && touch.position.y < Screen.height / 2) {
Player.Rotate (0, 0, 5);
}
The Opponent also is constantly moving in the forward direction based on the rotation angle, hence just need to rotate the opponent in the right direction
void Update(){
transform.position += transform.up * Time.deltaTime * EnemySpeed;
}
Here is my code for Opponent AI to find the collectible's position and rotate towards it
void AIControls ()
{
if (GameManager.Instance.CollectibleReady) {
//rotate towards collectible Item
Vector3 opponentPos = transform.position;
foodPos.x = foodPos.x - opponentPos.x;
foodPos.y = foodPos.y - opponentPos.y;
angle = Mathf.Atan2 (foodPos.y, foodPos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler (new Vector3 (0, 0, angle));
} else {
//Wander aimlessly avoid the player
}
}
This is not working correctly, The opponent wont rotate towards the newly spawned collectible item. Please Help!
I think Quaternion.LookRotation can help you - http://docs.unity3d.com/ScriptReference/Quaternion.LookRotation.html
So you could write something like:
var directionToFood = foodPos - transform.position;
transform.rotation = Quaternion.LookRotation(directionToFood, Vector3.up);
Ok so after playing around for a while I got what I wanted, adding the code here for someone who might find this useful.
void AIControls ()
{
if (GameManager.Instance.CollectibleReady) {
//rotate towards collectible Item
Vector3 opponentPos = Camera.main.WorldToScreenPoint (this.transform.position);
Vector3 foodtPos = Camera.main.WorldToScreenPoint (food.transform.position);
Vector3 relativePos = foodPos - opponentPos;
float angle = Mathf.Atan2 (relativePos.y, relativePos.x) * Mathf.Rad2Deg;
this.transform.rotation = Quaternion.Euler (new Vector3 (0, 0, angle - 90));
}
}
Thanks everyone for looking into the matter!
Related
i'm not really sure how to phrase the question sorry about that. I have encountered this hurdle while setting up animations on a character for learning a top down character controller (No rigid body) I've seen this question asked on Reddit and unity forums like a decade ago, but I don't really understand their answers.
My problem right now is that I can't find out a way to tell mecanim if the player is moving towards the direction they're facing. For example, if the player is moving left and aiming to the left, the moveForward animation should be played. If the player is moving left but aiming to the right, the moveBackwards animation should be played.
This is the first time i'm posting a question. I am sorry if my formatting is wrong.
Here is the code I have, I would greatly appreciate any help.
public class CharacterMovement : MonoBehaviour
{
private Vector3 velocity;
private Vector3 PlayerMoveInput;
Animator animationS;
[SerializeField] CharacterController characterrController;
[SerializeField] private float MoveSpeed;
[SerializeField] private float JumpHeight;
[SerializeField] private float Gravity = -9.81f;
// Start is called before the first frame update
void Start()
{
animationS = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
PlayerMoveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); //collects the input for the player
MovePlayer();
PlayerRotation();
}
private void MovePlayer()
{
if (characterrController.isGrounded)
{
velocity.y = -1f;
}
if (Input.GetKeyDown(KeyCode.Space) && characterrController.isGrounded)
{
velocity.y = JumpHeight;
}
///
else
{
velocity.y -= Gravity * -2f * Time.deltaTime;
}
Vector3 MoveVector = transform.TransformDirection(PlayerMoveInput);
characterrController.Move(MoveSpeed * Time.deltaTime * MoveVector);
characterrController.Move(velocity * Time.deltaTime);
float velocityX = Vector3.Dot(PlayerMoveInput, transform.forward);
float velocityZ = Vector3.Dot(PlayerMoveInput, transform.right);
animationS.SetFloat("velocityX", velocityZ, 0.1f, Time.deltaTime);
animationS.SetFloat("velocityZ", velocityX, 0.1f, Time.deltaTime);
}
void PlayerRotation()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
Debug.DrawRay(ray.origin, ray.direction, Color.yellow);
if (Physics.Raycast(ray, out hit))
{
Vector3 targetPosition = new Vector3(hit.point.x, transform.position.y, hit.point.z);
Quaternion rotation = Quaternion.LookRotation(targetPosition - transform.position);
transform.rotation = Quaternion.Lerp(transform.rotation, rotation, Time.deltaTime * 10.0f);
}
}
}
This isn't the exact implementation that would be used with your code but should give you a good start.
// get the angle between where the player is pointing, and where he is moving
float angle = Vector3.Angle(MoveVector, targetPosition - transform.position);
if (angle < 90f){
//move forward animation
} else {
//move back animation
}
I haven't tested this code but the idea is that if the angle between the direction the player is facing and the direction the player is moving is less than 90 degrees, then he is moving forward. Also the variables used here: MoveVector and targetPosition are private so you will need to fix that issue before this method can be implemented.
With mechanim, its very common to have 4 separate float value params in your animator. Two are used for movement info, two are for look direction info. This lets you use blend trees based on those functions that handle alot of the pain, all you need to do is update the values in the animator with the correct ones on each update/lateupdate depending on what you are doing.
This is how my animator params are layed out as well as the locomotion blend tree, I have another on a separate layer just for the head that uses Head left right, and head updown params to, you know, control the head.
And here is the code (or at least some of it) that sets the values on the animator component
EDIT:
Sorry, forgot to mention that the function i call "LerpFloat" on the anim variable is an extention method i defined myself for the animator component, all it does is gets the float value, lerps it, then sets the float value back. It just uses Mathf.Lerp.
Thank you both for your answers, the problem was the settings of the animation clips.....
Where you tick
Root transform Rotation
Root transform position (Y)
Root transform position (XZ)
I had ticked all of them, where just the root transform rotation was needed.
I will need to be more careful with the animator and animation clips in the future to avoid headaches.
I'm making a 3d pool game and I stuck with this problem.
I can't find the needed function to add an impulse to a ball which will make the ball to spin.
In other words, how to set the AIM point from where I want to add the impulse?
If I use AddForce or AddTorque, it seems that it calculates it from the Center of the Ball.
But how to specify the aim point (Left english, Right english, etc...)?
And how to route the ball to the camera direction after a cue hit the ball?
At first look I think you should check Rigidbody.AddForceAtPosition. You should calculate the aim points somehow in world coordinates also.
I am relatively new on Unity but the main idea came to my mind first rotate the cue vector by y axis by a small amount angle, then calculate the hit point for each regions by casting physics ray then finally apply impulse by using Rigidbody.AddForceAtPosition. I tried to write a sample code:
public class SphereController : MonoBehaviour {
private Vector3 cueStartPoint;
void Start () {
cueStartPoint = new Vector3(0, 0.5f, -13f);
}
void Update () {
//direction from cue to center
var direction = transform.position - cueStartPoint;
//rotate cue to aim right english
var angleForRightEnglish = 5f;
var directionForRight = Quaternion.AngleAxis(angleForRightEnglish, Vector3.up) * direction;
Debug.DrawRay(cueStartPoint, directionForRight, Color.red, 10f);
//rotate cue to aim right english
var angleForLeftEnglish = -5f;
var directionForLeft = Quaternion.AngleAxis(angleForLeftEnglish, Vector3.up) * direction;
Debug.DrawRay(cueStartPoint, directionForLeft, Color.blue, 10f);
//try a sample hit from right
if (Input.GetKeyDown(KeyCode.RightArrow)) {
var forceMagnitude = 1f;
var hitForce = forceMagnitude * directionForRight.normalized;
//calculate right hit point on sphere surface
RaycastHit hitInfo;
if (Physics.Raycast(cueStartPoint, directionForRight, out hitInfo)) {
var rightHitPoint = hitInfo.point;
gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hitForce, rightHitPoint, ForceMode.Impulse);
}
}
//try a sample hit from left
if (Input.GetKeyDown(KeyCode.LeftArrow)) {
var forceMagnitude = 1f;
var hitForce = forceMagnitude * directionForLeft.normalized;
//calculate left hit point on sphere surface
RaycastHit hitInfo;
if (Physics.Raycast(cueStartPoint, directionForLeft, out hitInfo)) {
var leftHitPoint = hitInfo.point;
gameObject.GetComponent<Rigidbody>().AddForceAtPosition(hitForce, leftHitPoint, ForceMode.Impulse);
}
}
}
}
this script should be added to ball as component of course, hope this helps.
I have a Child object, an Iron Bar (with Rigidbody and Gravity), connected to a Long Arm (w/ Rigidbody and isKinematic). I need to restrict the rotation of the Iron Bar from 1 to 40 degree angles only so it won't go overboard and hit the Long Arm. Please refer to attached image for more info. I tried several approaches from using a Hinge Joint and its Limit options and also through code. But I cannot seem to solve this problem. Right now I have this script attached to the Iron Bar but it does not seem to have any effect.
public Transform targetTransform;
private Vector3 _currentAngle;
private Vector3 _targetAngle;
float rotationX;
void FixedUpdate () {
transform.right = targetTransform.transform.right; //-- To make the Iron Bar follow player rotation
rotationX = transform.eulerAngles.x;
if (rotationX > 40) {
_targetAngle = new Vector3 (40, transform.eulerAngles.y, transform.eulerAngles.z);
_currentAngle = new Vector3 (Mathf.LerpAngle (transform.eulerAngles.x, _targetAngle.x, Time.deltaTime), Mathf.LerpAngle (transform.eulerAngles.y, _targetAngle.y, Time.deltaTime), Mathf.LerpAngle (transform.eulerAngles.z, _targetAngle.z, Time.deltaTime));
transform.eulerAngles = _currentAngle;
} else if (rotationX < 1) {
_targetAngle = new Vector3 (1, transform.eulerAngles.y, transform.eulerAngles.z);
_currentAngle = new Vector3 (Mathf.LerpAngle (transform.eulerAngles.x, _targetAngle.x, Time.deltaTime), Mathf.LerpAngle (transform.eulerAngles.y, _targetAngle.y, Time.deltaTime), Mathf.LerpAngle (transform.eulerAngles.z, _targetAngle.z, Time.deltaTime));
transform.eulerAngles = _currentAngle;
}
}
Would appreciate any help you can provide. Thanks for taking a look.
Note: This is a revised question but related to the initial one which I have already partially solved. Revision was made for further clarification.
The code I am using currently makes the enemy notice me at a distance then follow me if I get closer. The issue I am having is with how they move. I am building a Minecraft style game but I cant get the enemies to stay on the ground and jump up each block like I have too with the fps controller. They just seem to float the shortest distance possible towards me.
Code:
var target : Transform; //the enemy's target
var moveSpeed = 3; //move speed
var rotationSpeed = 3; //speed of turning
var range : float=10f;
var range2 : float=10f;
var stop : float=0;
var myTransform : Transform; //current transform data of this enemy
function Awake()
{
myTransform = transform; //cache transform data for easy access/preformance
}
function Start()
{
target = GameObject.FindWithTag("1Player").transform; //target the player
}
function Update () {
//rotate to look at the player
var distance = Vector3.Distance(myTransform.position, target.position);
if (distance<=range2 && distance>=range){
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
}
else if(distance<=range && distance>stop){
//move towards the player
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
}
else if (distance<=stop) {
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
}
}
Okay now here is what you are missing:
1.Lock the rotation on x and z axis to zero
myTransform.eulerAngles = new Vector3(0f, myTransform.eulerAngles.y, 0);
Just add this line at the end.
2.Make a short distance raycast in front of the AI and if it detects some obstacle, stop the current movement and move by y axis, if there is no obstacle move like you wrote in your script.
As for the raycasting goes, there is no need for me to write it here, you can find more info about it in Unity3D documentation here:
http://docs.unity3d.com/ScriptReference/Physics.Raycast.html and this tutorial is good for staters: https://unity3d.com/learn/tutorials/modules/beginner/physics/raycasting
At the moment I have a script which when you hit a cube, it follows the player...but when you stand still it overlaps you. What I want is to be able to set the position of the cube to five steps behind the player at all times...how would i do this?
GameObject.Find("Cube2").transform.position = Vector3(0.5, 0.5, 0.5);
That is what I have tried so far, but that just makes the cube disappear?
the script in its entirety:
static var target : Transform; //the enemy's target
var moveSpeed = 3; //move speed
var rotationSpeed = 3; //speed of turning
var Player = GameObject.Find("Player").transform.position;
var Cube2 = GameObject.Find("Cube2").transform.position;
var myTransform : Transform; //current transform data of this enemy
function Awake()
{
//myTransform = transform; //cache transform data for easy access/preformance
}
function Start()
{
//target = GameObject.FindWithTag("Player1").transform; //target the player
}
//var distance = Vector3.Distance(Player.transform.position, Cube2.transform.position);
//Debug.Log(distance);
function Update () {
Debug.Log(Player);
//var distance = Vector3.Distance(Player.transform.position, Cube2.transform.position);
//var distance = Vector3.Distance(player_distance, cube_distance);
// if (distance > 5)
// {
if (target == GameObject.FindWithTag("Player").transform)
{
//rotate to look at the player
GameObject.Find("Cube2").transform.position = Vector3(0.5, 0.5, 0.5);
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.LookRotation(target.position - myTransform.position), rotationSpeed*Time.deltaTime);
//move towards the player
myTransform.position += myTransform.forward * moveSpeed * Time.deltaTime;
}
//}
}
Like I said, I don't know unity really (had a 5 minute play with it)
In honesty it looks like you've pretty much got it - not sure why you can't get it working:
This is what should work: (assuming the quarternion calls are correct) - this is using latest Unity reference from the site so it might be diff to what works for you (what version of Unity you on?)
// Params
var moveSpeed = 3; // Move speed
var rotationSpeed = 3; // Speed of turning
// Find game objects
var Player = GameObject.Find("Player");
var Cube2 = GameObject.Find("Cube2");
function Update ()
{
// Vector from cube pos to player pos (vector math: target - position = vector to target from pos)
var dir = Player.transform.position - Cube2.transform.position;
// If the distance is over 5 units
if(dir.magnitude > 5.0f)
{
// Rotate towards player
Cube2.transform.rotation = Quaternion.Slerp(Cube2.transform.rotation, Quaternion.LookRotation(dir), rotationSpeed * Time.deltaTime);
// Move forward at specified speed
Cube2.transform.position += Cube2.transform.forward * moveSpeed * Time.deltaTime;
}
}
That should do it - if not let me know what happens (or if you get compilation errors) - like I said I don't really know Unity but I've had a look and I'm familiar with 3D/game programming