Creating a wave based 2d game and this is my current ai script. It works fine other than that whenever the enemy is coming from the right of the player the sprite is rotated upside down and I cant figure out how to fix it. Any help would be appreciated.
public class EnemyAI : MonoBehaviour{
public Transform target;
public int moveSpeed;
public int rotationSpeed;
private Transform myTransform;
// Use this for initialization
void Awake()
{
myTransform = transform;
}
void Start()
{
GameObject go = GameObject.FindGameObjectWithTag("Player");
target = go.transform;
}
// Update is called once per frame
void Update()
{
GameObject go = GameObject.FindGameObjectWithTag("Player");
if (go != null && target != null)
{
Vector3 dir = target.position - myTransform.position;
dir.z = 0.0f; // Only needed if objects don't share 'z' value
if (dir != Vector3.zero)
{
myTransform.rotation = Quaternion.Slerp(myTransform.rotation,
Quaternion.FromToRotation(Vector3.right, dir), rotationSpeed * Time.deltaTime);
}
else
{
this.enabled = false;
}
//Move Towards Target
myTransform.position += (target.position -
myTransform.position).normalized * moveSpeed * Time.deltaTime;
}
}
}
Related
I am making a game involving orbital physics. I was successfully able to implement this with a slightly modified version of Brackeys gravity tutorial https://youtu.be/Ouu3D_VHx9o, this is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class gravity : MonoBehaviour
{
public GameObject self;
public Rigidbody rb;
public Vector3 initialVelocity;
const float G = 66.74f;
public static List<gravity> Attractors;
public bool isAttractable;
private void Awake()
{
rb.AddForce(initialVelocity);
}
private void FixedUpdate()
{
//planets
if (isAttractable == false)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
//players, spaceships, astroids, ect
if (isAttractable == true)
{
foreach (gravity attractor in Attractors)
{
if (attractor != this)
Attract(attractor);
}
}
}
void OnEnable()
{
if( isAttractable == false)
{
if (Attractors == null)
Attractors = new List<gravity>();
Attractors.Add(this);
}
}
void OnDisable()
{
if (isAttractable == false)
{
Attractors.Remove(this);
}
}
void Attract(gravity objToAttract)
{
Rigidbody rbToAttract = objToAttract.rb;
Vector3 direction = -1 * (rb.position - rbToAttract.position);
Vector3 Force = direction.normalized * (G * ((rb.mass * rbToAttract.mass) / direction.sqrMagnitude));
rb.AddForce(Force);
}
public GameObject GetClosestPlanet()
{
GameObject close = null;
float minDist = Mathf.Infinity;
foreach (gravity attracor in Attractors)
{
float dist = Vector3.Distance(attracor.transform.position, transform.position);
if (dist < minDist)
{
close = attracor.transform.gameObject;
minDist = dist;
}
}
return close;
}
}
Then for player movement I used (and modified) Sebastian Lagues tutorial https://youtu.be/TicipSVT-T8,
this resulted in this code for the player controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerController : MonoBehaviour
{
public float mouseSensitivityX = 250f;
public float mouseSensitivityY = 250f;
Transform cameraT;
float verticalLookRot;
private Rigidbody rb;
Vector3 moveAmount;
Vector3 smootgMoveVelocity;
public float moveSpeed = 15;
public float jumpForce = 220;
public LayerMask groundedMask;
public bool grounded;
public GameObject currentPlanet;
private gravity playerGravity;
private void Awake()
{
rb = GetComponent<Rigidbody>();
playerGravity = GetComponent<gravity>();
Cursor.lockState = CursorLockMode.Locked;
cameraT = Camera.main.transform;
}
void Update()
{
currentPlanet = playerGravity.GetClosestPlanet();
//camera
transform.Rotate(Vector3.up * Input.GetAxis("Mouse X") * Time.deltaTime * mouseSensitivityX);
verticalLookRot += Input.GetAxis("Mouse Y") * Time.deltaTime * mouseSensitivityY;
verticalLookRot = Mathf.Clamp(verticalLookRot, -60, 60);
cameraT.localEulerAngles = Vector3.left * verticalLookRot;
//move input
Vector3 moveDir = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
Vector3 targetMoveAmount = moveDir * moveSpeed;
moveAmount = Vector3.SmoothDamp(targetMoveAmount, targetMoveAmount, ref smootgMoveVelocity, .15f);
//level on planet
if(currentPlanet != null)
{
transform.rotation = Quaternion.FromToRotation(transform.up, (transform.position - currentPlanet.transform.position).normalized) * transform.rotation;
}
//jump
if (Input.GetButtonDown("Jump"))
{ if(grounded)
{
rb.AddForce(transform.up * jumpForce);
print("u jumped");
}
}
}
private void FixedUpdate()
{
//move
rb.MovePosition(rb.position + transform.TransformDirection(moveAmount) * Time.fixedDeltaTime);
//check if on ground
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hit;
grounded = Physics.Raycast(ray, out hit, transform.localScale.y + 1.1f, groundedMask);
}
}
Now for the issue, this systems works fine when the planet the player is walking on is stationary. As in there are no other attracting bodys in the system and the planet has no initial velocity. However if the planet is moving the player will bounce up and down uncontrollably and will not be able to walk a certain distance away from the planets farthest point from its direction of movement. Here is a recording of this: https://youtu.be/noMekosb7CU
Does anyone know what is causing the bouncing and walking restrictions and how I can fix it?
Some notes on suggested solutions that haven't worked:
-set the planet as the players parent object, same results
-increase players mass, same results
-set the players velocity to += the planets velocity, same results or player goes into infinity
For me it seems to be working "correctly".
Looking like your player is attracted correctly and when the planet moves, your player is quickly moving towards the planet.
I think you could temporarily assign the player as a child gameobject to the planet he's walking on and he should probably move correctly along the planet coordinates and not on global coordinates. (If it works, you could just always assign the player as a child gameObject to every new planet that he visits)
My character has a chinemachine camera attached to it and it is moving perfectly fine. But when I move my mouse to change the camera direction it is not looking in that direction. And I can't figure out how to make it look in that direction.
I have a reference to the original camera at the top of the script by the name cam.
The script has different functions for movement, rotation, animation, etc.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//so that unity recognises the callbacks ctx passed to the movementinput
using UnityEngine.InputSystem;
public class AnimationAndMovController : MonoBehaviour
{
public Transform cam;
public float speed;
//here we are creadting three vars for the animation
//vector2 currentMovementInput stores the input axis of the player
//vector3 store the current position of the player
PlayerInput playerInput;
CharacterController characterController;
Animator animator;
Vector2 currentMovementInput;
Vector3 currentMovement;
Vector3 currentRunMovement;
Vector3 moveDir;
bool isMovementPressed;
bool isRunPressed;
float rotationFactor = 15.0f;
// float turnSmoothtime = 0.1f;
// float turnSmoothVelocity ;
float runMultiplier = 4.0f;
int walkHash ;
int runHash ;
//runs before start function
void Awake(){
playerInput = new PlayerInput();
characterController = GetComponent<CharacterController>();
animator = GetComponent<Animator>();
// walkHash = Animator.StringToHash("Walking");
// runHash = Animator.StringToHash("run");
//now instead of writing the logic three times we pass the callback ctx to the movementInput() function
playerInput.CharacterControls.Move.started += movementInput;
playerInput.CharacterControls.Move.canceled += movementInput;
playerInput.CharacterControls.Move.performed += movementInput;
playerInput.CharacterControls.Run.started += handleRun;
playerInput.CharacterControls.Run.canceled += handleRun;
}
void handleRun(InputAction.CallbackContext ctx){
isRunPressed = ctx.ReadValueAsButton();
}
//we are going to handle rotations with quaternions
void handleRotation(){
Vector3 positionToLookAt;
positionToLookAt.x = currentMovement.x;
positionToLookAt.y = 0.0f ;
positionToLookAt.z = currentMovement.z;
Vector3 direction = new Vector3(positionToLookAt.x, 0.0f, positionToLookAt.z).normalized;
Quaternion currentRotation = transform.rotation;
//we take the current rotation and the target rotation and slerp them *FYI : Im still not sure how slerp works
if(isMovementPressed){
Quaternion targetRotation = Quaternion.LookRotation(positionToLookAt);
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, rotationFactor * Time.deltaTime);
}
}
//we are passing the callback ctx to this function so that we dont have to call the function everytime we start, cancel or perform the movement
void movementInput(InputAction.CallbackContext ctx){
//we are setting the movement input to the axis of the player
currentMovementInput = ctx.ReadValue<Vector2>();
currentMovement.x = currentMovementInput.x;
//we are setting the z axis to the y axis of the player because we move y axis on keyboard or joystick but in game we move in z axis
currentMovement.z = currentMovementInput.y;
//now we are setting the run movement to the current movement
currentRunMovement.x = currentMovementInput.x * runMultiplier;
currentRunMovement.z = currentMovementInput.y * runMultiplier;
isMovementPressed = currentMovementInput.x != 0 || currentMovementInput.y != 0;
}
void handleAnimation(){
bool walk = animator.GetBool("walking");
bool run = animator.GetBool("run");
if(isMovementPressed && !walk){
animator.SetBool("walking", true);
}
else if(!isMovementPressed && walk){
animator.SetBool("walking", false);
}
if((isMovementPressed && isRunPressed) && !run){
animator.SetBool("run", true);
}
else if((!isMovementPressed || !isRunPressed)&& run){
animator.SetBool("run", false);
}
}
void handleGravity(){
//we are setting the gravity to -9.8f because we are moving in y axis
if(characterController.isGrounded){
float groundGravity = -0.05f;
currentMovement.y = groundGravity;
currentRunMovement.y = groundGravity;
}
else{
float gravity = -9.8f;
currentMovement.y += gravity * Time.deltaTime;
currentRunMovement.y += gravity * Time.deltaTime;
}
}
// Update is called once per frame
void Update()
{
handleAnimation();
handleRotation();
handleGravity();
if(isRunPressed){
characterController.Move(currentRunMovement * Time.deltaTime);
}
else{
characterController.Move(currentMovement * Time.deltaTime);
}
}
//we are checking if the player script gets enabled or disabled and accordingly we are enabling or disabling the player input
void OnEnable(){
playerInput.CharacterControls.Enable();
}
void OnDisable(){
playerInput.CharacterControls.Disable();
}
}
That quite a lot of code to dive into.
I'd try:
Quaternion cameraRot = Camera.Main.transform.rotation;
transform.rotation = cameraRot;
Or if you have a target Quaternion.LookRotation:
Vector3 relativePos = target.position - transform.position;
// the second argument, upwards, defaults to Vector3.up
Quaternion rotation = Quaternion.LookRotation(relativePos, Vector3.up);
transform.rotation = rotation;
Hope that helps
Use Transform.LookAt, which points a Game Object's rotation towards a target's position.
In your case, this would be
transform.LookAt(cam);
here is the code that I use..
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveCamera : MonoBehaviour
{
public float sensitivity = 1000f;
public float xRotation = 0f;
public Transform playerBody;
public Quaternion localRotate;
// Start is called before the first frame update
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update()
{
float MX = Input.GetAxis("Mouse X")*sensitivity*Time.deltaTime;
float MY = Input.GetAxis("Mouse Y")*sensitivity*Time.deltaTime;
xRotation -= MY;
xRotation = Mathf.Clamp(xRotation,-90f,90f);
transform.localRotation = Quaternion.Euler(xRotation,0f,0f);
localRotate = transform.localRotation;
playerBody.Rotate(Vector3.up * MX);
}
}
the player body you see is the player object to you character...
note that the camera is use is not In. cinema chine and is inside of the player...
So I was trying to make a game just for fun and to learn for future use. So I encountered this problem in making the enemy NPC. I want it to follow me or chase me but I want the NPC to only move horizontal and vertical and I want the NPC to move per tile as well just like my Player.
Here's the video of how it looks
https://www.youtube.com/watch?v=CB_vdt1Z3nA
and here's the NPC script
public class ChaseScript : MonoBehaviour
{
public float speed;
private GameObject player;
private Transform player_transform;
void Start()
{
player = GameObject.Find("Player");
}
void Update()
{
player_transform = player.GetComponent<Transform>();
transform.position = Vector3.MoveTowards(transform.position, player_transform.position, speed * Time.deltaTime);
}
}
Here's my player controller
public void InputMove()
{
if (!isMoving)
{
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
if (input.x != 0) input.y = 0;
if (input != Vector2.zero)
{
playerAnimation.SetParameterValue(animator);
var movePos = transform.position;
movePos.x += input.x;
movePos.y += input.y;
FacingForward.transform.position = movePos;
if (IsWalkable(movePos))
StartCoroutine(Move(movePos));
}
playerAnimation.SetParameterValueisMoving(animator);
}
if (Input.GetKey(KeyCode.LeftShift))
{
moveSpeed = 6f;
animator.speed = 1.5f;
}
else
{
moveSpeed = 4f;
animator.speed = 1f;
}
}
IEnumerator Move(Vector3 movePos)
{
isMoving = true;
while ((movePos - transform.position).sqrMagnitude > Mathf.Epsilon)
{
transform.position = Vector3.MoveTowards(transform.position, movePos, moveSpeed * Time.deltaTime);
yield return null;
}
transform.position = movePos;
isMoving = false;
}
private bool IsWalkable(Vector3 movePos)
{
if (Physics2D.OverlapCircle(movePos, 0.1f, SolidObjectLayer | NPC) != null)
{
return false;
}
return true;
}
What I did to my Player to move per tile is I just add 1 to transform so It'll be a constant movement but I don't know how to apply it on the NPC with the Vector3.MoveTowards but if it's not possible to do then it's fine
Check if this could work (you can adapt it to your 2D case)
using UnityEngine;
public class ChaseOrthoScript : MonoBehaviour
{
public float speed;
private GameObject player;
private Transform player_transform;
bool isMoving = false;
void Start()
{
player = GameObject.Find("Player");
player_transform = player.GetComponent<Transform>();
transform.LookAt(player_transform.position);
}
void Update()
{
if (transform.InverseTransformPoint(player_transform.position).z > 0) {
transform.position += transform.forward * speed * Time.deltaTime;
isMoving = true;
} else {
if (isMoving) {
float angle = Vector3.Angle(transform.forward, player_transform.position - transform.position);
transform.Rotate(Vector3.up, Mathf.Sign(angle) * 90);
isMoving = false;
} else if (transform.InverseTransformPoint(player_transform.position).z <= 0) { //player is back
transform.Rotate(Vector3.up, 180);
}
}
}
}
note that the player_transform = player.GetComponent<Transform>(); is moved to the Start(). Usually you dont want GetComponents in an update as you need to get it only once. ALso its much cleaner to have a public GameObject player; variable in the script and attach the reference in the editor that the player = GameObject.Find("Player");. Usually you dont want scene elements found by a hardcoded magic value in your code.
Hope that helps.
I'm trying to make a 2D Space Shooter game in Unity but stumbled upon a bug that needs fixing. I'm using Unity 2021.1.0f1 and the new Input System in Unity and I'm trying to implement movement for my character. When I press the WASD keys, my character for some reason moves 15 units on every press. I don't want that, I want smooth movement for my character. Here's my code:
using UnityEngine;
using UnityEngine.InputSystem;
public class Ship : MonoBehaviour
{
private Keyboard _keyboard = Keyboard.current;
[SerializeField] private float speed = 0.25f;
[SerializeField] private GameObject projectile;
[SerializeField] private Rigidbody2D rigidbody;
private void Start()
{
if (speed == null)
{
Debug.Log("Please assign a a value to \"speed\".");
}
if (rigidbody == null)
{
Debug.Log("Please assign a a value to \"rigidbody\".");
}
if (projectile == null)
{
Debug.Log("Please assign a a value to \"projectile\".");
}
}
public void OnMove(InputAction.CallbackContext context)
{
Vector2 movementVector = context.ReadValue<Vector2>();
Vector3 move = Quaternion.Euler(0.0f, transform.eulerAngles.y, 0.0f) * new Vector3(movementVector.x, movementVector.y, 0.0f);
transform.position += move * speed;
}
public void OnFire(InputAction.CallbackContext context)
{
Vector2 spawnPosition = new Vector2(transform.position.x, 0.5f);
if (context.performed)
{
Instantiate(projectile, spawnPosition, Quaternion.identity);
}
}
}
Anyone?
I have been working on the same project lately and I used the following code for the movement of my spaceship and it moves smoothly :
void C_Movement(){
//Input controller (direction)
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical") ;
Vector3 directionX = new Vector3(horizontalInput,0,0) ;
transform.Translate(directionX* Time.deltaTime * 13) ;
Vector3 directionY = new Vector3(0,verticalInput,0) ;
transform.Translate(directionY* Time.deltaTime * 13) ;
}
This has been asked about 1 million times, yet I can't find a solution to my issue, rather to my annoyance.
Help is very much appreciated as I'm not sure whats going wrong and how to solve it any more.
Script 1:
public class Something : MonoBehaviour {
[SerializeField]
private Rigidbody cannonballInstance;
public ProjectileController projectile;
public Transform firePoint;
[SerializeField]
[Range(10f, 80f)]
private float angle = 45f;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
FireCannonAtPoint(hitInfo.point);
}
}
}
private void FireCannonAtPoint(Vector3 point)
{
var velocity = BallisticVelocity(point, angle);
Debug.Log("Firing at " + point + " velocity " + velocity);
ProjectileController newProjectile = Instantiate(cannonballInstance, transform.position, transform.rotation) as ProjectileController;
//cannonballInstance.transform.position = transform.position;
//cannonballInstance.velocity = velocity;
}
private Vector3 BallisticVelocity(Vector3 destination, float angle)
{
Vector3 dir = destination - transform.position; // get Target Direction
float height = dir.y; // get height difference
dir.y = 0; // retain only the horizontal difference
float dist = dir.magnitude; // get horizontal direction
float a = angle * Mathf.Deg2Rad; // Convert angle to radians
dir.y = dist * Mathf.Tan(a); // set dir to the elevation angle.
dist += height / Mathf.Tan(a); // Correction for small height differences
// Calculate the velocity magnitude
float velocity = Mathf.Sqrt(dist * Physics.gravity.magnitude / Mathf.Sin(2 * a));
return velocity * dir.normalized; // Return a normalized vector.
}
The following is called from the previos when instansiated, causes error is this due to the type i'm trying to create or what?
Script 2:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProjectileController : MonoBehaviour {
public float speed;
private Vector3 oldVelocity;
private Rigidbody rigidbodyTemp;
private int bounceLimit = 3;
// Use this for initialization
void Start () {
rigidbodyTemp = GetComponent<Rigidbody>();
rigidbodyTemp.isKinematic = false;
rigidbodyTemp.freezeRotation = true;
rigidbodyTemp.detectCollisions = true;
}
// Update is called once per frame
void FixedUpdate () {
rigidbodyTemp.AddForce(transform.forward * speed);
oldVelocity = rigidbodyTemp.velocity;
}
private void OnCollisionEnter(Collision collision)
{
bounceLimit -= 1;
if (collision.gameObject.tag == "Bulllet") // Check if hit another bullet
{
Destroy(this.gameObject);
}
else if (collision.gameObject.tag == "Crate") // Check if a Crate has been hit, will hold power ups
{
Destroy(this.gameObject);
Destroy(collision.gameObject);
PickUUpBounce.isActive = true;
}
else if (collision.gameObject.tag == "Player") // Check if hit a player
{
Destroy(this.gameObject);
}
else if (collision.gameObject.tag == "Enemy") // Check if enemy is hit
{
Destroy(this.gameObject);
Destroy(collision.gameObject);
}
else if (bounceLimit == 0) // check if bounce limit is reached
{
Destroy(this.gameObject);
}
else // bounce
{
Vector3 reflectedVelocity;
Quaternion rotation;
ContactPoint contact = collision.contacts[0]; // stores contact point for reflected velocity
reflectedVelocity = Vector3.Reflect(oldVelocity, contact.normal); // reflected velocity equals a reflection of the old velocity around the contact point
rigidbodyTemp.velocity = reflectedVelocity; // Change rigidbody velocity
rotation = Quaternion.FromToRotation(oldVelocity, reflectedVelocity); // old directyion -> new direction
transform.rotation = rotation * transform.rotation; // front face always facing the front
}
}
}
The prefab (cannonballInstance) you are instantiating is declared as Rigidbody. When you call the Instantiate function and pass cannonballInstance to it, it will return a Rigidbody not ProjectileController.
ProjectileController is a script. You can't cast the returned Rigidbody to ProjectileController. You have to use GetComponent to retrieve the ProjectileController instance that is attached to your prefab(cannonballInstance) .
ProjectileController newProjectile = Instantiate(cannonballInstance, transform.position, transform.rotation).GetComponent<ProjectileController>();
It's better to break that line of code into pieces to make it easier to debug just in case anything is null.
Rigidbody rg = Instantiate(cannonballInstance, transform.position, transform.rotation);
ProjectileController newProjectile = rg.GetComponent<ProjectileController>();