I have designed an attack method for a player that is full functional, however I am new to AI and have no idea as to where I would begin when it comes to adapting this into a state for an FSM.
protected void UpdateAttackState()
{
// check for input
float rot = transform.localEulerAngles.y + rotationSpeed * Time.deltaTime * Input.GetAxis("Horizontal");
Vector3 fwd = transform.forward * moveSpeed * Time.deltaTime * Input.GetAxis("Vertical");
// Tank Chassis is rigidbody, use MoveRotation and MovePosition
GetComponent<Rigidbody>().MoveRotation(Quaternion.AngleAxis(rot, Vector3.up));
GetComponent<Rigidbody>().MovePosition(_rigidbody.position + fwd);
if (turret) {
Plane playerPlane = new Plane(Vector3.up, transform.position + new Vector3(0, 0, 0));
// Generate a ray from the cursor position
Ray RayCast = Camera.main.ScreenPointToRay(Input.mousePosition);
// Determine the point where the cursor ray intersects the plane.
float HitDist = 0;
// If the ray is parallel to the plane, Raycast will return false.
if (playerPlane.Raycast(RayCast, out HitDist))
{
// Get the point along the ray that hits the calculated distance.
Vector3 RayHitPoint = RayCast.GetPoint(HitDist);
Quaternion targetRotation = Quaternion.LookRotation(RayHitPoint - transform.position);
turret.transform.rotation = Quaternion.Slerp(turret.transform.rotation, targetRotation, Time.deltaTime* turretRotSpeed);
}
}
if(Input.GetButton("Fire1"))
{
if (elapsedTime >= shootRate)
{
//Reset the time
elapsedTime = 0.0f;
//Also Instantiate over the PhotonNetwork
if ((bulletSpawnPoint) & (bullet))
Instantiate(bullet, bulletSpawnPoint.transform.position, bulletSpawnPoint.transform.rotation);
}
}
// Update the time
elapsedTime += Time.deltaTime;
}
This is completely dependent on how you want your state machine to work. An example of this would be the AI trying to seek and destroy other players.
Seeking State: AI would move around the map with a detection zone (collider) that would change state on trigger enter. This would then save a position of the detection's generalized location which would be used to move and rotate the AI tank towards that position.
Firing State: AI enters a new detection zone (collider) within firing range, turret will move to aim at target. AI will also move around the map to keep firing range at a maximum while also keeping track of the last known position of the enemy if they happen to go out of range.
Detecting Fire: Player or other AI fires their weapon this could increase the detection radius resulting in a seeking state being called.
After those functions are figured out, place a switch or something similar within the Update() to allow everything within the AI to run alongside Unity's functions.
Related
Currently I made bullet shoot from players position. Problem is that it shoots from player's center and not from the gun. I changed position of the bullet to make it shoot out from the gun. But the problem is that, when I start to rotate the player, bullet copies the rotation but not the position of the gun. It just stays on the same place. Here's my code:
public Transform firePoint;
public GameObject bulletPrefab;
public float bulletForce = 20f;
public void Shoot()
{
Vector3 newPosition = new Vector3(firePoint.position.x + 1, firePoint.position.y - 0.1f, firePoint.position.z);
GameObject bullet = Instantiate(bulletPrefab, newPosition, firePoint.rotation);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firePoint.right * bulletForce, ForceMode2D.Impulse);
yield return new WaitForSeconds(0.5f);
}
Your issue is that you have hard-coded offset (x = 1, y = -0.1f) change. When you rotate your player 90 degrees, the offset for gun end becomes different.
There are two solutions for this:
Solution #1
Place your fire point Transform at the tip of the gun, make it child of the gun. That way the Transform will always follow the end of the gun.
Now, for instantiating, you can use firepoint.position without any modifications.
There are some drawbacks to this, mainly having strict objects hierarchy and it becomes harder to dynamically change guns as you will have to find and reassign fire point for each of them.
Solution #2
Have Vector3 firePointOffset;.
Once instantiating, calcualte the position of the fire point by doing
var firePointPosition = playerTransform.position + Vector3.Scale(playerTransform.forward, firePointOffset)`;
transform.forward returns Vector3 that points, well, forward for that specific transform. Vector3.Scale allows multiplying two vectors x * x, y * y, z * z;
I'm trying to implement player movement such that the player can't collide with other collidable objects, such as tiles, but the recasts I'm using aren't registering any other collider when it clearly should be
So the problem I'm trying to solve is to check if there is a collision with a raycast that extends exactly how far the player will move, and get information on the object that was collided with. After that, I would adjust the player's movement accordingly so that the player is not colliding with the object after the raycast finds such a collidable. Here is the source code:
// Move body towards a certain direction, by a certain value
protected void MoveBody(Vector3 direction, float speed)
{
Vector3 newPos = transform.position + (direction * speed * Time.deltaTime);
//int layerMask = 1 << 8; // bit sequence 1000 0000 - Only 'collidable' layer
RaycastHit hit;
Vector3 ySize = new Vector3(0, GetComponent<BoxCollider2D>().size.y/2, 0);
Vector3 startPos = transform.position - ySize;
if (Physics.Raycast(startPos, direction, out hit, speed * Time.deltaTime))
{
Debug.DrawRay(startPos, direction * speed * Time.deltaTime, Color.yellow);
Debug.Log("Did Hit");
}
else
{
Debug.DrawRay(startPos, direction * speed * Time.deltaTime, Color.yellow);
Debug.Log("Did Not Hit");
}
// Update gameObject's transform.position here based on whether a collidable was found
}
When I run the game, the first frame ensures that the raycast is colliding with another box collider, as shown by this image:
The expected Debug.Log would be "Did hit", but it's logging "Did not hit". The yellow line shows the full raycast, and it's clearly intersecting the box collider of the tile below the purple man.
The MoveBody function is being run each frame to simulate gravity (I omitted the part where the object falls so the player object just stays in place for the entire time), but as you can see, the raycast never registers a collision. Both player and tile1 have a box collider, so I don't understand what's going on. Would really appreciate any help I can get!!! Thanks!
The problem was that I was using Physics.Raycast, but I should have been using Physics2D.Raycast, as my Unity game is in 2D. Physics.Raycast isn't compatible with 2D Unity games.
i want the player to look into the direction, the camera is looking.
The camera follows the player (3rd person game style).
I've tried it with
transform.localRotation = new Quaternion(transform.localRotation.x,
cameraMain.transform.localRotation.y,
transform.localRotation.z,
transform.localRotation.w);
but it doesn't work.
Sometimes the player starts rotating the other direction.
the following code will make the object (specified in the parameter) face in the direction of where the main camera is looking:
public void lookInDirectionOfCamera(Transform object) {
RayCastHit hit;
if (Physics.raycast(cameraMain.transform.position, cameraMain.transform.forward, out hit)) {
object.forward = hit.point - object.position;
}else { //if the raycast didnt hit anything, make the object face 100 units in front of the camera
Vector3 point = Camera.main.transform.position + Camera.main.transform.forward * 100;
object.forward = point - object.position;
}
}
This will make the player face the point that is forward to the camera. If you just want them to have the same rotation in the y-axis don't use Quaternions!
Instead, you can just do it with Euler angles:
transform.eulerAngles = new Vector3(transform.eulerAngles.x,
cameraMain.transform.eulerAngles.y,
transform.eulerAngles.y);
The reason not to use transform.localRotation is because that is a Quaternion. The y component in a Quaternion is not the same as the y-axis in a Euler angle (what you are used to seeing), Quaternions are very confusing so you should almost never set individual values in them. If you want to edit them only use the built-in methods.
Get the direction the camera is looking with cameraMain.transform.forward, make a copy with a y value of zero, then use Quaternion.SetLookRotation to look in that direction with the global up direction.:
Vector3 cameraDirection = cameraMain.transform.forward;
Vector3 characterLookDirection = new Vector3(cameraDirection.x,
0f,
cameraDirection.z);
Quaternion newRotation = new Quaternion();
newRotation.SetLookRotation(characterLookDirection, Vector3.up);
transform.rotation = newRotation;
I'm building a top down shooter and so I have my camera above my player and the map. Here's the code I've written in the player controller script for movement:
public class playerMovement : MonoBehaviour {
public float speed;
private Camera mainCamera;
void Start () {
mainCamera = FindObjectOfType<Camera>();
}
// Update is called once per frame
void Update () {
// player movement
transform.Translate(speed * Input.GetAxis("Horizontal") * Time.deltaTime, 0f, speed * Input.GetAxis("Vertical") * Time.deltaTime);
// Camera Ray casting
Ray cameraRay = mainCamera.ScreenPointToRay(Input.mousePosition);
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
float rayLength;
if (groundPlane.Raycast(cameraRay, out rayLength)) {
Vector3 look = cameraRay.GetPoint(rayLength);
Debug.DrawLine(cameraRay.origin, look, Color.red);
transform.LookAt(new Vector3(look.x, transform.position.y, look.z));
}
}
}
I want to be able to move the player using the WASD keys and also rotate following the direction on where the mouse is, however I don't want the rotation of the player to change the direction of the keys, I need the player to move forwards if the W key is pressed no matter which way the player is facing.
However for some reason my code makes the player move forwards depending on which way it is facing which I don't want.
How can I fix this?
The problem is that your transform.Translate call is in "self" space. Forward, backward, left, right are all relative to the direction the transform is facing. That is why your player is moving relative to the facing direction.
If you want to translate relative to "global" or "world" space, you have to add an additional parameter.
// player movement
transform.Translate(speed * Input.GetAxis("Horizontal") * Time.deltaTime,
0f,
speed * Input.GetAxis("Vertical") * Time.deltaTime,
Space.World);
Note the Space.World parameter at the end, to set the world coordinate system.
You can find more in the Unity docs here: https://docs.unity3d.com/ScriptReference/Transform.Translate.html
You need to look at the difference between local and global coordinate systems.
Right now your WASD keys are moving the player character according to global coordinates, and you want the WASD movement to be dependant on the player's orientation so you need to use a local coordinate system.
http://wiki.unity3d.com/index.php?title=Converting_Between_Coordinate_Systems
I am making a game in C# in unity where the player uses an Xbox 360 controller to control a character, I can rotate the player easily like this using the right joystick:
if(Input.GetAxis("RightJoystickX")!=0 && Input.GetAxis("RightJoystickY")!=0)
{
float horizontal = Input.GetAxis("RightJoystickX") * Time.deltaTime;
float vertical = Input.GetAxis("RightJoystickY") * Time.deltaTime;
float angle = Mathf.Atan2(vertical, horizontal) * Mathf.Rad2Deg;
characterController.transform.eulerAngles = new Vector3(0, newAngle, 0);
}
however, every time I release the joystick and then move it again, it immediately jumps to that new position and doesn't add the rotation to the previous rotation. This is a problem as the player can only move forwards in the direction the character is rotation, i need to be able to add rotations to the previous state without jumping to a new rotation.