I got a small arena where the player can move on. At the sides of the area there are spawners. These spawners instantiate bombs and should throw them at the player.
For the direction I actually use
transform.lookAt(playerTransform);
So this is a rough map
So the spawners are rotating around the map. They move from one point to the next point.
My bomb object got a rigidbody attached and the gravity is activated. I just need to find out how to make a spawner throwing a bomb to the player.
public class BombSpawner : MonoBehaviour
{
[Range(0, 3)]
[SerializeField]
private int nextPointIndex; // set the first targetpoint
private Vector3[] targetPoints = {
new Vector3(-15,0,15),
new Vector3(15,0,15),
new Vector3(15,0,-15),
new Vector3(-15,0,-15)};
private float movementSpeed = 10;
GameObject bombPrefab;
Transform player;
private void Start()
{
bombPrefab = Resources.Load(StringCollection.BOMB) as GameObject;
player = Globals.GetPlayerObject().transform;
}
private void Update()
{
transform.LookAt(player); // set the object rotation
Vector3 nextPoint = targetPoints[nextPointIndex]; // get the target point
transform.position = Vector3.MoveTowards(transform.position, nextPoint, movementSpeed * Time.deltaTime); // move the spawner
if (transform.position == nextPoint) // point reached? set a new point
{
if (nextPointIndex < targetPoints.Length - 1)
nextPointIndex++;
else
nextPointIndex = 0;
}
}
}
So I could write a method like this
void SpawnBomb()
{
GameObject spawnedBomb = Instantiate(bombPrefab);
}
but how do I achieve the throwing mechanic? For a first try, the targetPoint is player.position that should be fine.
You need to get the direction from spawner to the current position of the target, spawn the bomb and add force to that bomb using the direction you just got.
In order to do this you should substract your spawner's position from your target position.
Vector3 dir = target.transform.position - transform.position;
Now that you have the direction you can spawn your bomb and AddForce() to it. To add force you need to call the Rigidbody component of your spawned Bomb, like this:
spawnedBomb.GetComponent<Rigidbody>().AddForce(dir.normalized * force, ForceMode.Impulse);
Where dir is the direction towards the target (normalized - so the distance doesn't matter) and force is pretty much the speed of the bomb.
Here you can read more about Rigidbody.AddForce.
Related
I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.
I'm pretty new to Unity. I tried to create a script that the camera would follow the actor (with a little difference). Is there a way to improve the code? It works just fine. But I wonder if I did it the best way. I want to do it about as I wrote, so if you have any tips. Thank you
Maybe change Update to FixedUpdate ?
public GameObject player;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("Cube"); // The player
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(player.transform.position.x, player.transform.position.y + 5, player.transform.position.z - 10);
}
Making the camera following the player is quite straight forward.
Add this script to your main camera.
Drag the reference of the player object to the script and then you are done.
You can change the values in the Vector 3 depending on how far you want the camera to be from the player.
using UnityEngine;
public class Follow_player : MonoBehaviour {
public Transform player;
// Update is called once per frame
void Update () {
transform.position = player.transform.position + new Vector3(0, 1, -5);
}
}
Follows player in the back constantly when the player rotates, no parenting needed, with smoothing.
Piece of Knowledges:
Apparently, Quaternion * Vector3 is going to rotate the point of the Vector3 around the origin of the Vector3 by the angle of the Quaternion
The Lerp method in Vector3 and Quaternion stand for linear interpolation, where the first parameter gets closer to the second parameter by the amount of third parameter each frame.
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.125f;
public Vector3 locationOffset;
public Vector3 rotationOffset;
void FixedUpdate()
{
Vector3 desiredPosition = target.position + target.rotation * locationOffset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
transform.position = smoothedPosition;
Quaternion desiredrotation = target.rotation * Quaternion.Euler(rotationOffset);
Quaternion smoothedrotation = Quaternion.Lerp(transform.rotation, desiredrotation, smoothSpeed);
transform.rotation = smoothedrotation;
}
}
This will always follow the player from the same direction, and if the player rotates it will still stay the same. This may be good for top-down or side-scrolling view, but the camera setup seems to be more fitting for 3rd person, in which case you'd want to rotate the camera when the player turns.
The easiest way to do this is actually not with code alone, simply make the camera a child of the player object, that way its position relative to the player will always stay the same!
If you do want to do it through code, you can change the code to be like this:
void Update()
{
Vector3 back = -player.transform.forward;
back.y = 0.5f; // this determines how high. Increase for higher view angle.
transform.position = player.transform.position - back * distance;
transform.forward = player.transform.position - transform.position;
}
You get the direction of the back of the player (opposite of transform's forward). Then you increase the height a little so the angle will be a bit from above like in your example. Last you set the camera's position to be the player's position and add the back direction multiplied by the distance. That will place the camera behind the player.
You also need to rotate the camera so it points at the player, and that's the last line - setting the camera's forward direction to point at the player.
Here is just another option. I always find it easier to have the variables which are populated in the inspector so you can adjust it and fine tune it as needed.
public GameObject player;
[SerializeField]
private float xAxis, yAxis, zAxis;
private void Update()
{
transform.position = new Vector3(player.transform.position.x + xAxis, player.transform.position.y + yAxis, player.transform.position.z + zAxis);
}
I am having trouble getting the enemy's projectile to fly from the enemy to the player's position. When I play the game, the enemy bullet projectiles fly off in one direction on the screen and not toward the player. I think the issue might be in how I am assigning direction to the projectile prefab? Any suggestions would be much appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyController : MonoBehaviour
{
public float speed;
public Rigidbody enemyRb;
[SerializeField] float rateOfFire;
private GameObject player;
public GameObject projectilePrefab;
float nextFireAllowed;
public bool canFire;
Transform enemyMuzzle;
void Awake()
{
enemyRb = GetComponent<Rigidbody>();
player = GameObject.Find("Player");
enemyMuzzle = transform.Find("EnemyMuzzle");
}
void Update()
{
//move enemy rigidbody toward player
Vector3 lookDirection = (player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * speed);
//overallSpeed
Vector3 horizontalVelocity = enemyRb.velocity;
horizontalVelocity = new Vector3(enemyRb.velocity.x, 0, enemyRb.velocity.z);
// turns enemy to look at player
transform.LookAt(player.transform);
//launches projectile toward player
projectilePrefab.transform.Translate(lookDirection * speed * Time.deltaTime);
Instantiate(projectilePrefab, transform.position, projectilePrefab.transform.rotation);
}
public virtual void Fire()
{
canFire = false;
if (Time.time < nextFireAllowed)
return;
nextFireAllowed = Time.time + rateOfFire;
//instantiate the projectile;
Instantiate(projectilePrefab, enemyMuzzle.position, enemyMuzzle.rotation);
canFire = true;
}
}
It looks like what is actually happening is that you create a bunch of bullets but don't store a reference to them. So each bullets sits in one place while the enemy moves closer to the player ( which might give the appearance that the bullets are moving relative to the enemy. ) I also assume the enemy is moving very fast since it is not scaled by delta time but is being updated every frame.
I think projectilePrefab is just the template object you're spawning, so you probably don't want to move it directly and you certainly don't want to instantiate a new bullet every frame.
If you want to move the object you spawned the least changes ( but still problematic ) from your example code might be:
public class EnemyController : MonoBehaviour
{
// add a reference
private GameObject projectileGameObject = null;
void Update()
{
//Update position of spawned projectile rather than the template
if(projectileGameObject != null ) {
projectileGameObject.transform.Translate(lookDirection * speed * Time.deltaTime);
}
// Be sure to remove this extra instantiate
//Instantiate(projectilePrefab, transform.position, projectilePrefab.transform.rotation);
}
public virtual void Fire()
{
//instantiate the projectile
projectileGameObject = Instantiate(projectilePrefab, enemyMuzzle.position, enemyMuzzle.rotation);
}
}
Or keep multiple bullets in a list. This implementation has the bug that it will always use the current enemy to player vector as the direction rather than the direction that existed when it was fired.
What you will probably want eventually is that each projectile is has it's own class script to handle projectile logic. All the enemyController class has to do is spawn the projectile and sets it's direction and position on a separate monobehavior that lives on the Projectile objects that handles it's own updates.
I am trying to make a 2d plat-former where you see the player from the side. I want him to be continuously moving and you have to press space at the right time so he doesn't fall. Right now everything works but he doesn't collide with the ground. I want it to be like he's running behind a wall so I want to ignore a certain layer I have made and collide with the boxes below that. So far I have tried ray casting, watched multiple tutorials, and did box collisions. Box collisions worked but to get all the platforms counted as solid I'd need like 50 box colliders. Here is my current code:
public int playerSpeed = 10;
public int playerJumpPower = 1250;
public float moveX;
public float playerYSize = 2;
public LayerMask mainGround;
public float playerFallSpeed = 5;
void Awake(){
}
// Update is called once per frame
void Update()
{
RaycastHit2D hit = Physics2D.Raycast(transform.position, new Vector2(10, 0));
if(hit.distance < 0.7f){
print("hi");
}
Vector3 characterTargetPosition = new Vector3(transform.position.x + playerSpeed, transform.position.y, transform.position.z);
transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerSpeed * Time.deltaTime);
if(Input.GetKeyDown("space")){
// float playerTargetPosY = transform.position.y + playerJumpPower;
// Vector3 characterTargetPosition = new Vector3(transform.position.x, playerTargetPosY, transform.position.z);
// transform.position = Vector3.Lerp(transform.position, characterTargetPosition, playerJumpPower * Time.deltaTime);
gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * playerJumpPower);
}
//PlayerMove();
}
I have a rigidBody2D on my player so right now he just falls through the ground but the jump does work. If there is any easy way to do this. Like some script, a tutorial, or website I'm open for it. Please help.
Do you have a Rigidbody2D in your player? Things that will move usually have to have a RigidBody
(sorry for posting this as an answer. Cant comment yet)
EDIT:
try this:
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
//Physics usually are done in FixedUpdate to be more constant
public void FixedUpdate(){
if (Input.GetKeyDown("space"))
{
if(!rb.simulated)
//player can fall
rb.simulated = true;
rb.AddForce(Vector2.up * playerJumpPower);
}
else
{
//third argument is the distance from the center of the object where it will collide
//therefore you want the distance from the center to the bottom of the sprite
//which is half of the player height if the center is actually in the center of the sprite
RaycastHit2D hit = Physics2D.Raycast(transform.position, -Vector2.up, playerYSize / 2);
if (hit.collider)
{
//make player stop falling
rb.simulated = false;
}
}
}
If the player is the only thing that will collide with something you can just take out the colliders from the object that the player will not collide with.
Else you can check for the layer of the collided object with hit.collider.gameObject.layer and decide if the player will collide with that layer or not
(note that you have to compare with the index of the layer. If you want to get the index by its name you can use LayerMask.NameToLayer(/*layer name*/))
you will have to do rb.simulated = true everytime you want to do something with the RigidBody (like AddForce())
hope it helped :)
I am trying to create a script in Unity that when another object touches it, it grabs the object, rotates it in a random direction, and launches it, but I only want to rotate and launch the other object on the z axis.
Here is my script:
public BallController ballController;
private GameObject ball;
private Rigidbody2D ballRb;
void OnTriggerEnter2D(Collider2D other)
{
ball = other.gameObject;
ballRb = ball.GetComponent<Rigidbody2D>();
ball.transform.position = this.transform.position;
// Freeze the ball
ballRb.velocity = Vector2.zero;
// Rotate the ball
ball.transform.rotation = Quaternion.Euler(0, 0, Random.Range(0, 360));
// Start moving the ball again
ballRb.velocity = ballRb.velocity * ballController.bulletSpeed * Time.deltatime;
}
The other script (The ball) is has a Ridgidbody and is launched by another object into this one, the script gets the ball to rotate how I want to, but it won't get the ball moving again.
The ballController is set in the editor and the bulletSpeed is just an int that I want the ball to travel at (currently set to 6).
When dealing with Rigidbody2D you shouldn't use the transform for setting the rotation but rather use ballRb.rotation.
Later you are using
ballRb.velocity = ballRb.velocity * ballController.bulletSpeed * Time.deltatime;
but right before you have set
ballRb = Vector2.zero;
So the multiplication results in Vector2.zero. Also adding Time.deltaTime(typo btw) in this one-time assignment makes no sense.
Also if you remove this line you are not taking the new rotation into account when assigning a new velocity.
The velocity is in global space. You also can't use e.g. transform.right as new direction since the transform isn't updated .. the Rigidbody2D is .. so you can use GetRelativeVector in order to set the new local direction after rotating
private void OnTriggerEnter2D(Collider2D ball)
{
// The assignment of the GameObject
// was kind of redundant except
// you need the reference for something else later
var ballRb = ball.GetComponent<Rigidbody2D>();
// set position through RigidBody component
ballRb.position = this.transform.position;
// Rotate the ball using the Rigidbody component
// Was the int overload intented here? Returning int values 0-359
// otherwise rather use the float overload by passing float values as parameters
ballRb.rotation = Random.Range(0f, 360f);
// Start moving the ball again
// with a Vector different to Vector2.zero
// depending on your setup e.g.
ballRb.velocity = ballRb.GetRelativeVector(Vector2.right * ballController.bulletSpeed);
}
little demo
for the walls btw I used something very similar:
var ballRb = ball.GetComponent<Rigidbody2D>();
var newDirection = Vector2.Reflect(ballRb.velocity, transform.up);
ballRb.rotation = ballRb.rotation + Vector2.SignedAngle(ballRb.velocity, newDirection);
ballRb.velocity = newDirection.normalized * ballController.bulletSpeed;