Im making a simple 2d ship game for my school project. Right now the ai will face the player and follow them at a certain distance, then when close enough, rotate so that it can fire at the player from each side of the ship. But with my current code the enemy will move sideways towards the player at the firing range, which obviously shouldn't happen.
So how can i make the enemy only move forwards and remain perpendicular to the player.
Here is my current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
public float speed, stopD, retreatD, Chase_range, distance,firing_range;
// declare player and enemy as transform to retrieve their positions
public Transform player;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
Chase_range = 14;
firing_range = 10;
stopD = 6;
retreatD = 4;
}
// Update is called once per frame
void Update()
{
// Distance between the enemy and player positions
distance = (Vector2.Distance(transform.position, player.position));
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
if (distance > stopD)
{
// move towards the player if the distance is greater than stop distance
speed = 10;
}
else if (distance < stopD & distance > retreatD)
{
speed = 0;
}
else if (distance < retreatD)
{
// move away from the player if the distance is less than stop distance (avoid collision)
speed = -10;
}
if (distance > Chase_range)
{
// have to use vector3 since vector2 does not work with the z axis
// calculate the difference in the position vectors
Vector3 vectorToTarget = player.position - transform.position;
// Calculate the angle between the objects and convert the result to degrees
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
// convert the angle into a quaternion so that it can be used by slerp
Quaternion playerq = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, playerq, Time.deltaTime * speed);
}
if (distance < firing_range)
{
float Rfactor;
Vector3 vectorToTarget = player.position - transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
if (angle < 0)
{
Rfactor = 90;
}
else if (angle > 0)
{
Rfactor = -90;
}
Quaternion playerq = Quaternion.AngleAxis(angle + Rfactor, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, playerq, Time.deltaTime * speed);
}
}
And don't mind the shitty comments, its for school.
Any other comments about the code would also be greatly appreciated
With some quick googling I found this answer:
https://answers.unity.com/questions/1125044/how-do-i-move-an-object-relative-to-another-object.html
Using this you can move the enemy forward relative to the player. If you rotate the player now you will have the desired result: the enemy will move with the player but stay focused on it
Related
So, Hey guys I am new to unity. I have a small doubt. I have a player who is a child of the topcube in a 3 stackcube placed upon eachother.
These cubes have a target position to move once the user clicks on them.
Like imagine there are 3 points in my world. POINT A with location coordinates as(0,0,1),POINT B with (0,0,2),POINT C with (0,0,3) and the 3 stack cube is place on (0,0,0) with the player attached as a child to topcube in that 3stackcube.
All these points(A,B,C) has a script called targetpoint with a variable bool isFilled(default as false) in them which becomes true when one of the cube reaches to its target position.
Further im checking whenever the cubes reaches their target position make isFilled true and check to see if there is a child attached if yes get the animator of the child and trigger jump animation. The jump animation is an inplace animation.
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)when the cube he is attached reached its target position while also playing jump animation.
I did a code. it doesnt seem to be working. And sorry for huge paragraphs. Im totally new to coding and asking doubts. Any help will be helpful thanks.
[SerializeField] public List<targetpoint> waypoints;
[SerializeField] float moveSpeed = 2f;
[SerializeField] AudioClip[] surferSounds;
[SerializeField] GameObject particleToPlay;
int waypointIndex = 0;
float t;
//Cached Reference
AudioSource audioSource;
//State
public bool move = false;
void Start()
{
transform.position = this.transform.position;
t = 0f;
}
void FixedUpdate()
{
if (move == true)
{
MoveTarget();
}
}
void MoveTarget()
{
//Time.timeScale = 0.1f;
if (waypointIndex <= waypoints.Count - 1)
{
var targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
if (transform.position == targetPosition)
{
//Debug.Log(t);
if (waypoints[waypointIndex].isFilled == false)
{
waypoints[waypointIndex].isFilled = true;
AudioClip clip = surferSounds[UnityEngine.Random.Range(0, surferSounds.Length)];
var storeToDestroy = Instantiate(particleToPlay, targetPosition, Quaternion.identity);
Destroy(storeToDestroy , 5f);
audioSource.PlayOneShot(clip);
move = false;
}
else if(waypoints[waypointIndex].isFilled == true)
{
waypointIndex++;
targetPosition = waypoints[waypointIndex].transform.position;
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveSpeed * Time.deltaTime);
}
if (this.gameObject.transform.childCount > 0)
{
var storeChild = gameObject.transform.GetChild(1).gameObject;
StartCoroutine(GravityAndJump(storeChild,storeChild.transform.position+1*transform.forward,1f));
}
else
{
return;
}
}
}
}
IEnumerator GravityAndJump(GameObject child, Vector3 newPosition , float time)
{
var elapsedTime = 0f;
var startingPosition = child.transform.position;
while(elapsedTime < time)
{
child.GetComponent<Animator>().SetTrigger("shouldJump?");
child.transform.position = Vector3.Lerp(startingPosition, newPosition, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
//storeChild.GetComponent<Animator>().SetFloat("JumpSpeed", 1f);
//yield return new WaitForSeconds(1f);
//gameObject.GetComponentInChildren<Rigidbody>().useGravity = true;
}
}
So I want to programmatically move my character +1 towards the direction he is facing (if he is facing z move + 1 in z, if x move +1 in x like this)
You can get the forward direction for a GameObject using with transform.forward you can use this to calculate target position in front of the GameObject.
Set target position some distance in front of the transform
targetPosition = transform.position + (transform.forward * distance);
Move towards target position at some speed.
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * followSpeed);
Determining arrival to targetPosition
When it comes to determining if transform has arrive to target destination you should measure the distance instead of comparing that the vectors are the same.
So replace this:
if (transform.position == targetPosition){}
With something like this:
if(Vector3.Distance(transform.position, targetPosition) < .001f){
transform.position = targetPosition;
}
Unless you strictly set two vectors to same values it's likely that they will never be considered equal due to how floating point numbers work.
Im trying to make a peice of code that instantiates a gameobject, set its rotation to face the direction of the cursor from the player character at that moment and move towards that direction with constant speed for 2 seconds then stop. However my piece of coded is moving the gameobject towards the cursor direction but the speed gets changed depending on how far my cursor is from the player character.
private IEnumerator Rake()
{
Vector3 relativepos =
Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativepos, Vector3.up);
float timepassed = 0;
GameObject WcastRB =
Instantiate(Wcast, gameObject.transform.position, rotation);
Rigidbody2D rg;
rg = WcastRB.GetComponent<Rigidbody2D>();
while (timepassed < 2)
{
timepassed += Time.deltaTime;
rg.velocity = WcastRB.transform.forward * 1000 * Time.deltaTime;
if (timepassed >= 2)
{
rg.velocity = WcastRB.transform.forward * 0;
}
yield return null;
}
}
this is what I have made.
Try this out, I found that because transform.forward was dependent on the object's rotation(and subsequently the original click position).
When the click was too close to the object the transform.forward Vector2 had a magnitude(length) of less than one, causing the speed to slow down.
By increasing the magnitude of the velocity to exactly 1f it should go consistent speeds in all directions
IEnumerator Rake()
{
Vector3 relativepos = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativepos, Vector3.up);
float timepassed = 0;
GameObject WcastRB = Instantiate(Wcast, gameObject.transform.position, rotation);
Rigidbody2D rg;
rg = WcastRB.GetComponent<Rigidbody2D>();
Vector2 velocity = rg.transform.forward;
velocity.Normalize();
while (timepassed < 2)
{
timepassed += Time.deltaTime;
rg.velocity = velocity * 1000 * Time.deltaTime;
if (timepassed >= 2)
{
rg.velocity = new Vector2();
}
yield return null;
}
}
Edit:
Removed self-implemented .Normalize() because I forgot that i also used the built in .Normalize() literally right before it.
This is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// Tweekable move speed
public float MoveSpeed = 4;
// Set what distance it should be from the target
int MaxDist = 10;
int MinDist = 5;
// This is the target
public Transform Mouse;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// Keep looking at an object
transform.LookAt();
// Turn off gravity
Physics2D.gravity = Vector2.zero;
// When distance is over minimun
if (Vector3.Distance(Mouse.position, Mouse.position) >= MinDist)
{
// Keep distance
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
// When it's between max and minimun
if (Vector3.Distance(transform.position, Mouse.position) <= MaxDist)
{
}
}
}
// I imported this code from another project so the enemy keeps looking at the player, and keeps his distance. it was a 3D project, so there may be more errors, and this is also my first time creating a game. Be gentle
I am working on a similar project right now.
Here is the code I use to rotate the player (first part) and then move it into that direction (second part):
public Rigidbody2D rb;
public float speed;
void Update()
{
//get mouse position
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//calculating the angle
float AngleRad = Mathf.Atan2(mousePos.y - transform.position.y, mousePos.x -
transform.position.x);
float AngleDeg = (180 / Mathf.PI) * AngleRad;
//rotating the player
Player.transform.rotation = Quaternion.Euler(0, 0, AngleDeg);
//second part
Vector3 mouseDir = mousePos - transform.position;
mouseDir.z = 0f;
mouseDir = mouseDir.normalized;
rb.AddForce(mouseDir * speed);
}
If the player rotates, but it is looking in the wrong way (in my case), then play around with the the last line of the first part. The following code worked fine for me:
transform.rotation = Quaternion.Euler(0, 0, AngleDeg - 90);
If you want the player to move at a constant speed, then you can use
rb.velocity = mouseDir * speed;
instead of
rb.AddForce(mouseDir * speed);
in the last line of the second part.
Your Player-Gameobject needs a Rigidbody2D component. I also added an if-statement around the second part, so the player only moves to the wanted direction when (for example) space is pressed, but always looks to the mouse.
So far I have a character controller that enables me to move around, sprint and crouch (no animation) , but I am unable to get the controller to jump. I know 100% the character is getting the input to jump, and the movement vector is around ~40 on the Y axis, so the player should be moving. The problem is, nothing happens. The player can still move around, and fall of ledges, but nothing happens when i press space
This is my code:
using UnityEngine;
public class KeyboardMovement : MonoBehaviour
{
private CharacterController controller;
public float walkSpeed;
public float sprintSpeed;
public float crouchSpeed;
public float jumpHeight;
Vector3 up = Vector3.zero;
Vector3 movement = Vector3.zero;
Vector3 forward = Vector3.zero;
Vector3 sideways = Vector3.zero;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
float speed = walkSpeed;
//If crouching, set speed to crouch speed. This ovverides sprinting
if (SingletonSettings.GetKey(SingletonSettings.Keybindings.crouch))
speed = crouchSpeed;
//Else if sprinting, set speed to sprint speed
else if (SingletonSettings.GetKey(SingletonSettings.Keybindings.sprint))
speed = sprintSpeed;
//Create vectors for movement
forward = transform.TransformDirection(Vector3.forward) * Input.GetAxis("Vertical");
sideways = transform.TransformDirection(Vector3.right) * Input.GetAxis("Horizontal");
//up = SingletonSettings.GetKey(SingletonSettings.Keybindings.jump) && controller.isGrounded ? Vector3.up * jumpHeight : Vector3.zero;
movement = (up * 100) + ((forward + sideways) * 10 * Time.deltaTime * speed);
//Combine vectors and Multiply by DeltaTime to ensure smooth movement at different framerates.
//Also multiply by 10*speed to ensure correct speed in different states
if (controller.isGrounded && Input.GetKey(KeyCode.Space))
{
movement.y = jumpHeight*50 * Time.deltaTime;
}
controller.SimpleMove(movement);
}
void OnGUI()
{
GUILayout.Label('\n' + Newtonsoft.Json.JsonConvert.SerializeObject(movement.y, Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonSerializerSettings
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}));
}
}
CharacterController.SimpleMove from the docs
Moves the character with speed.
Velocity along the y-axis is ignored.
Gravity is automatically applied.
https://docs.unity3d.com/ScriptReference/CharacterController.SimpleMove.html
You should use CharacterController.Move
A more complex move function taking absolute movement deltas.
Attempts to move the controller by motion, the motion will only be constrained by collisions. It will slide along colliders
...
This function does not apply any gravity.
https://docs.unity3d.com/ScriptReference/CharacterController.Move.html
With this method, you'll need to apply gravity yourself
movement.y -= gravity * Time.deltaTime;
controller.Move(movement * Time.deltaTime);
I'm new to Unity and have a problem I can't figure out. I want objects to spawn randomly at a distance of 20 from a FPS player. You could say the objects need to spawn on the surface of a half sphere with the player as the center. But: not all of that sphere can be used. The "highest" part is too high for objects to spawn, so basically it's a sphere with the top cut off.
What I tried:
thePosition = Random.onUnitSphere * 20 + object2.transform.position;
Obviously, this takes into account the whole sphere (should be only half a sphere) and doesn't take into account the "cut off" part.
So I thought: I basically want to make a ray that can pivot on the ground (so the max angle is 360°), and can go up and down, with a max angle of 90°. Think of it like a canon that can turn (pivot) and go up/down with an angle. Here's an image of what I mean:
So I tried:
Vector3 raydirection = new Vector3 (1f, 1f, 0);
raydirection = Quaternion.Euler (45, 0, 0) * raydirection;
Ray ray = new Ray (player.transform.position, raydirection);
thePosition = ray.GetPoint (20);
But that doesn't allow me to control the pivot angle (angle 1) and the "up-down" angle (angle 2) separately.
So my question is: how can I make it so that I can control both angles of this ray? Because if I can do that, I can just take a random number between 0 and 360 for the pivoting part, and between 0 and 90 for the up/down part.
Any help is much appreciated!
Coincidentally, I needed something very similar to this. The following Behavior will spawn a certain prefab (objectToSpawn) exactly spawnCount times within the set parameters.
The helper class (bottom code) generates a Vector from Yaw, Pitch and a Vector (basically the distance in your case).
What it does:
Pick a random direction (yaw and pitch) within set parameters
Pick a random distance (sounds like you can omit this step)
Calculate the vector
Spawn object
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RandomSpawner : MonoBehaviour {
public GameObject objectToSpawn;
public int spawnCount;
public float minDistance = 2;
public float maxDistance = 10;
public float minPitchDegrees = 0;
public float maxPitchDegrees = 45;
public float minYawDegrees = -180;
public float maxYawDegrees = 180;
void Start ()
{
for (int i = 0; i < spawnCount; i++)
{
float distance = minDistance + Random.value * (maxDistance - minDistance);
float yaw = minYawDegrees + Random.value * (maxYawDegrees - minYawDegrees);
float pitch = minPitchDegrees + Random.value * (maxPitchDegrees - minPitchDegrees);
Vector3 position = RotationHelper.ConvertYawPitch (Vector3.forward * distance, yaw, pitch);
Instantiate (objectToSpawn, position, Quaternion.identity);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class RotationHelper {
public static Vector3 ConvertYawPitch(Vector3 vector, float yaw, float pitch)
{
Quaternion yawRotation = Quaternion.AngleAxis (yaw, Vector3.up);
Vector3 yawedZAxis = yawRotation * Vector3.left;
Quaternion pitchRotation = Quaternion.AngleAxis (pitch, yawedZAxis);
Vector3 yawedVector = yawRotation * vector;
Vector3 position = pitchRotation * yawedVector;
return position;
}
}
In your specific case, the parameters should be:
minDistance = 20
maxDistance = 20
minPitchDegrees = 0
maxPitchDegrees = 0-90, whatever the angle is after you "cut off the top"
minYawDegrees = -180
maxYawDegrees = 180
I want objects to spawn randomly at a distance of 20 from a FPS
player.
What I understood from this is that you want to spawn objects on the ground, distant 20 units away from the player, in random directions.
You could say the objects need to spawn on the surface of a half
sphere with the player as the center.
Now, this is just another way to make things complex. No need to use the sphere to solve this.
If you want to spawn objects on the surface, easiest solution will be to get a random angle in relation with Vector3.up and walk for 20 units to find the desired point.
Script:
public class Spawner : MonoBehaviour
{
public Transform player;
public Transform prefab;
[Range(10,50)]
public float distance = 20f;
IEnumerator Start()
{
while (true)
{
yield return new WaitForSeconds(0.05f);
Spawn();
}
}
[ContextMenu("Spawn")]
public void Spawn()
{
Vector3 spawnPoint = FindPoint(player.position, distance, Random.Range(0, 360));
Instantiate(prefab, spawnPoint, Quaternion.identity, transform);
}
[ContextMenu("Clear")]
public void Clear()
{
foreach (var item in transform.GetComponentsInChildren<Transform>())
{
if (item != transform)
DestroyImmediate(item.gameObject);
}
}
Vector3 FindPoint(Vector3 center, float radius, int angle)
{
return center + Quaternion.AngleAxis(angle, Vector3.up) * (Vector3.right * radius);
}
}
Result:
Calculates random point based on player's position:
Hope this helps :)