I am trying to get my GameObject (asteroid) to spawn continuously in the game. I looked up and followed this tutorial: https://pressstart.vip/tutorials/2018/09/25/58/spawning-obstacles.html. To make the asteroid move, I created this script (AsteroidObject) and to spawn the objects, I created this script (DeployAsteroids). There are no errors and the Debug.Log appears in the console. But the asteroid game object cannot be seen and will not spawn. Anyone can help? Thanks in advance!
Asteroid Object Codes:
public class AsteroidObject : MonoBehaviour
{
public float speed = 10.0f; //how fast the asteroid will move
private Rigidbody2D rb;
private Vector2 screenBounds; //screenbounds calculation
// Start is called before the first frame update
void Start()
{
rb = this.GetComponent<Rigidbody2D>(); //find rigidbody 2d and set it to rb reference by using the getcomponent
rb.velocity = new Vector2(-speed, 0); //moving the asteroid from right to left by setting the x value, leaving the y value 0 so that it will not move up and down
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z)); //defines the boundaries of the screen on the x and y axis
}
// Update is called once per frame
void Update()
{
//transform.position = new Vector3(Mathf.Clamp(transform.position.x, -9f, 9f),
//Mathf.Clamp(transform.position.y, -4f, 4f), transform.position.z);
if (transform.position.x < screenBounds.x) //check if it is moving to the left of the screen
{
Destroy(this.gameObject);
Debug.Log("hello world");
}
}
}
DeployAsteroid codes:
public class DeployAsteroids : MonoBehaviour
{
public GameObject asteroidPrefab;
public float respawnTime = 1.0f;
private Vector2 screenBounds;
// Start is called before the first frame update
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
StartCoroutine(asteroidWave());
}
private void spawnEnemy()
{
GameObject a = Instantiate(asteroidPrefab) as GameObject;
a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y));
}
IEnumerator asteroidWave()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
spawnEnemy();
//Debug.Log("Hello World");
}
}
}
You even say it yourself
the Debug.Log appears in the console.
Well, you log in the moment you destroy your object => it is already gone.
Destroy(this.gameObject);
Debug.Log("hello world");
Unless you mean the out commented log after the spawn method. You still immediately destroy the object. I think you would spot it immediately if you would use useful logs and not twice the same one
Debug.Log("Spawned a new object");
and
Debug.Log("Destroyed an object");
So what exactly is happening then?
You immediately destroy the new spawned objects!
you do
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
which stores the position at the right border of the screen.
Let's say e.g. somewhere at x = 2; (total random imaginary example number).
Then right after spawning you set it to
a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y));
So for the x you set it to (using our example value) x = -4;
This is not even the left boarder of the screen but even beyond!
Additionally you tell the asteroid to move
rb.velocity = new Vector2(-speed, 0);
so assuming the value speed is positive even more into negative x direction.
And finally you do
// Even without the Rigidbody this is already comparing
// if(-2 * screenBounds.x < screenBounds.x)
// Or with our example numbers
// if(-4 < 2)
if (transform.position.x < screenBounds.x)
{
Destroy(this.gameObject);
Debug.Log("hello world");
}
=> This condition will always be true and immediately destroys your objects in the next frame.
So what should I do instead?
I assume you are trying to spawn the asteroid at the right boarder.
And want to destroy it after it passed the left boarder. So it should probably be
private void spawnEnemy()
{
GameObject a = Instantiate(asteroidPrefab);
Vector3 rightEdgeWorldPoint = Camera.main.ScreenToWorldPoint(
new Vector3(Screen.width, Random.Range(0, Screen.height),
Camera.main.nearClipPlane);
rightEdgeWorldPoint.z = 0f;
a.transform.position = rightEdgeWorldPoint;
}
And in the asteroid
if (Camera.main.WorldToScreenPoint(transform.position).x < 0)
{
Destroy(this.gameObject);
Debug.Log("Left the screen -> destroyed");
}
Note: Typed on smartphone but I hope the idea gets clear
Go to the scene hierarchy and check if the asteroids can be seen there. May be you do not see them within the game or the scene camera, but if they are in the scene they are spawn somewhere. It would be interesting info to update in the question.
I did not dig into the code but if they are there, you need to check or set the position in which they are spawned respect to the camera, for the asterioids to be in the field of view.
Check the posistion given in this line: a.transform.position = new Vector2(screenBounds.x * -2, Random.Range(-screenBounds.y, screenBounds.y)); and if it fits in the camera field of view.
Related
so I'm making a 2D platformer and I would like to spawn enemies on the whole map but not in the camera range.
I have to mention I am using Cinemachine to follow the player.
This is my script for the spawner:
*public class MobSpawner : MonoBehaviour
{
public GameObject Mob;
float randX;
Vector2 whereToSpawn;
public float spawnRate = 2f;
float nextSpawn = 0.0f;
public GameObject Camera;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Time.time > nextSpawn)
{
nextSpawn = Time.time + spawnRate;
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
Instantiate(Mob, whereToSpawn, Quaternion.identity);
}
}
}*
As you can see I'm taking 2 points and in between them I spawn enemies. As this range would basically be the entire map, the camera will always be in it.
I want enemies to NOT spawn inside the camera yet spawn outside of it. How can I manage to do this?
I'd say the simplest solution would be to just calculate vector distance between the player position and spawning position. Since this is a 2d space and you are spawning them only on one axis it should be fine.
So you could do
float distanceAwayFromCameraView = 5;
// you will need a method, reference or just get the object via tag
var playerPos = GameObject.Find("Player").transform.position;
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
while ((Vector2.Distance(whereToSpawn, playerPos) < distanceAwayFromCameraView){
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
}
Instantiate...
I'm not at my pc to test this right now, please let me know if there's errors
i try to make a simple 3D Rail Shooter, and i currently stuck on how to spawn the bullets for my Ship.
I want when i press on fire that the bullet gets spawned and gets shooted.
I created an empty gameObject as a child of my ship and put a script on it.
The problem is i currently stuck and i can't figure out how to get it done.
So im asking for help, what did i miss, what did i wrong?
Here is the script i came up with:
public Rigidbody rb;
private bool isMoving = false;
private bool isFirePressed = false;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.useGravity = false;
}
void Update()
{
isFirePressed = Input.GetButtonDown("Fire1");
}
void FixedUpdate()
{
if (Input.GetButtonDown("Fire1"))
{
// the cube is going to move upwards in 10 units per second
rb.velocity = new Vector3(0, 0, 100);
isMoving = true;
Debug.Log("fire");
}
}
First I guess you wanted to use your variable isFirePressed.
Then if that is a prefab I guess you rather want to Instantiate a new bullet:
if (isFirePressed)
{
var newBullet = Instantiate (rb, transform);
// the cube is going to move upwards in 10 units per second
newBullet.velocity = new Vector3(0, 0, 100);
newBullet.useGravity = false;
isMoving = true;
Debug.Log("fire");
}
You changed the velocity on the prefab which doesn't work.
Additionally note that the velocity is in World-Space coordinates. So currently you are shooting in world Z direction no matter where your plane is facing to.
I would rather use e.g.
newBullet.velocity = transform.forward * 100;
to shoot it in the direction your BulletEmitter is facing towards.
I'm trying to make a patrolling AI character that will move from point to point.
The patrol part works perfectly. However, the problem is that the sprite only faces right. When it turned the sprite stays facing the same direction.
I have tried to change the transform rotation using transform.rotate, transform.rotation, transform.Quaternion and making a variable to store the rotation value yet they all kick errors back. The errors are usually made from the rotate/rotation functions not being compatible with any of the attempts I have tried.
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// To do:
/// - make rotation of enemy sprite work when reaching the end of patrol area
/// - create attack function
/// </summary>
public class Enemy : MonoBehaviour
{
public int health;
public float speed;
public GameObject bloodEffect;
public Transform[] moveSpots; //patrol spots
public float startWaitTime; //start countdown till move to next spot
private Rigidbody2D rb;
private Animator anim;
private int randomSpot; //number of patrol spots
private float waitTime; //how long enemy stays at patrol spot for
// Start is called before the first frame update
void Start()
{
waitTime = startWaitTime; //make waittime equal to startwaittime
anim = GetComponent<Animator>();
randomSpot = Random.Range(0, moveSpots.Length); //choose a random first spot
}
// Update is called once per frame
void Update()
{
Vector3 spriteRotation = new Vector3(0, randomSpot, 0);
transform.position = Vector2.MoveTowards(transform.position, moveSpots[randomSpot].position, speed * Time.deltaTime); //move toward first patrol area
transform.eulerAngles = spriteRotation;
if (Vector2.Distance(transform.position, moveSpots[randomSpot].position) < 0.5f) //asks if patrol point is further that .5f away from enemy
{
if (waitTime <= 0) //if waitTime less than or equal to 0
{
randomSpot = Random.Range(0, moveSpots.Length); //picks new patrol point
waitTime = startWaitTime; //restarts countdown clock
}
else
{
waitTime -= Time.deltaTime; //counts down clock till next point
}
}
if (health <= 0)
{
Destroy(gameObject);
}
}
public void TakeDamage(int damage)
{
Instantiate(bloodEffect, transform.position, Quaternion.identity);
Debug.Log("Blood effect played");
health -= damage;
Debug.Log("Damage Taken");
}
}
The expected results for this code is that a random point will be chosen and the AI will move toward that chosen point. Once there it will stay idle for a specified amount of time before turning and moving to a new spot.
The actual result is mostly the same as expected only the sprite does not turn around but instead continues to face to the right even when the AI is moving left.
Image of area
the enemy is the dark red cube, the movepoints are the points that the enemy patrols between. when it reaches the left point, he should turn to the right and go back but this is not what happens, instead he just moves back and forth with no rotation. ive tried the SpriteRenderer.flipX route and it only works one time and then sticks with that direction.
The SpriteRenderer Component has a Flip attribute you could use for this.
You can access it in code
SpriteRenderer.flipX = true;
It will only flip the sprite and won't change anything else, so double check if your colliders are still in the right space :) See more in the documentation
Good luck
randomSpot is an index not an angle. So using
transform.eulerAngles = new Vector3(0, randomSpot, 0);
doens't make any sense to me ...
Instead of rotating you could also flip the sprite/Image by using a negative scale like e.g.
// Update is called once per frame
private void Update()
{
// however you determin if facing left or right
// you e.g. simply check whether the target position
// is left or right of you
var difference = moveSpots[randomSpot].position - transform.position;
var isFacingRight = difference.x > 0;
if (isFacingRight && transform.localScale.x < 0
|| !isFacingRight && transform.localScale.x > 0)
{
FlipSprite();
}
}
private void FlipSprite()
{
// invert the local X-axis scale
transform.localScale = new Vector3(-spriteTransform.localScale.x, spriteTransform.localScale.y, spriteTransform.localScale.z);
}
Script used for the example
private void Update()
{
// works only in a ScreenOverlay Canvas
var targetPosition = Input.mousePosition;
var difference = targetPosition - transform.position;
var isFacingRight = difference.x > 0 ? true : false;
if (isFacingRight && transform.localScale.x < 0
|| !isFacingRight && transform.localScale.x > 0)
{
FlipSprite();
}
// simply only move left or right on the x-axis towards the mouse
transform.position = Vector3.MoveTowards(transform.position, new Vector3(targetPosition.x, 218, 0), Time.deltaTime * 100);
}
private void FlipSprite()
{
// invert the local X-axis scale
transform.localScale = new Vector3(-transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
You can try:
if (moveSpot[randomSpot].transform.position.x > transform.position.x) {
transform.localScale = new Vector3(-1, 1, 1);
}
else {
transform.localScale = new Vector3(1, 1, 1);
}
I am newbie to Unity. In my project i have instantiates a ball in the player position. From the Player Position the ball should go to random position 40 to 90 degrees. if the player is in 0,0,0 position. the target should be 10,0,0.
I have done the code. But it is not perfect. I have done something wrong.
randomly it should set the target.
public class Movetowards3: MonoBehaviour {
// Use this for initialization
GameObject go;
public GameObject prefab;
public GameObject Target;
public GameObject endPos1;
public Vector3 endpos2;
float speed = 5f;
public bool s=false;
public GameObject player;
public bool s2=false;
void Start () {
go = Instantiate(prefab, player.transform.localPosition, Quaternion.identity);
}
// Update is called once per frame
void Update () {
player=GameObject.FindGameObjectWithTag("Player");
if(s==true)
{
go = Instantiate(prefab, player.transform.localPosition, Quaternion.identity);
endpos2 =new Vector3(player.transform.position.x+10f,player.transform.position.y,player.transform.position.z);
s=false;
}
if(go.transform.position != endpos2)
{
Vector3 newPos = Vector3.MoveTowards(go.transform.localPosition, endpos2,speed * Time.deltaTime);
go.transform.position = newPos;
}
}
}
I have incremented the value of x, but i think it is not correct.
At its core your code is correct, however the issue is that endpos does not have the chance to instantiate.
I think this is your issue:
Here:
public bool s=false;
You set s to false and you never change it later. This becomes a problem here:
if(s==true)
{
go = Instantiate(prefab, player.transform.localPosition, Quaternion.identity);
endpos2 =new Vector3(player.transform.position.x+10f,player.transform.position.y,player.transform.position.z);
s=false;
}
And, you need these to instantiate to start what you do in the next if statement:
if(go.transform.position != endpos2)
{
Vector3 newPos = Vector3.MoveTowards(go.transform.localPosition, endpos2,speed * Time.deltaTime);
go.transform.position = newPos;
}
So I would recommend that you either make public bool s=true;
Or add and else statement to the first if statement.
Also just a side comment, you instantiate go twice, I don't know if thats intentional but they are doing the same thing.
I'm doing a 2D platform game where the player only climbs (y-axis).
What I'm trying to do is when I reach near to the top edge, my camera goes up a little bit so I can see more of the level.
I have this, but it's not working:
Public Transform player;
Void Start() { }
Void Update()
{
if (player.transform.position > Screen.height - 50)
{
transform.position.y += speed * Time.deltaTime;
}
}
Other Way is like this But not working more than once, i might need to set move = false; but dont know how without interupting Lerp:
float moveTime = 1f; // In seconds
float moveTimer = 0.0f; // Used for keeping track of time
bool moving = false; // Flag letting us know if we're moving
public float heightChange = 7.0f; // This is the delta
// These will be assigned when a collision occurs
Vector3 target; // our target position
Vector3 startPos; // our starting position
void Start()
{
}
void Update()
{
// If we're currently moving and the movement hasn't finished
if (moving && moveTimer < moveTime)
{
// Accumulate the frame time, making the timer tick up
moveTimer += Time.deltaTime;
// calculate our ratio ("t")
float t = moveTimer / moveTime;
transform.position = Vector3.Lerp(startPos, target, t);
}
else
{
// We either haven't started moving, or have finished moving
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (!moving)
{
// We set the target to be ten units above our current position
target = transform.position + Vector3.up * heightChange;
// And save our start position (because our actual position will be changing)
startPos = transform.position;
// Set the flag so that the movement starts
moving = true;
}
}
}
you are comparing two different things here. Screen.height is in pixels while player position is in world units. so lets assume your screen is 1024x768 you are checking if your player is above 718 which is a huge amount in world units.
What you have to do is convert one to the others unit and compare then for example with http://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html
Another thing i noticed is that this script is named player so i assume this is controlling your player object in which case
transform.position.y += speed * Time.deltaTime;
would only change your players position. To change the position of the main camera you could use:
Camera.main.transform.position += new Vector3(0f, speed * Time.deltaTime, 0f);
And lastly, always post the errors you get when asking questions, explain what you expect to happen and what actually happens.
You could try lerping the between positions eg.
transform.position = new trasform.lerp(transform.position, player.transform.position, speed*Time.DeltaTime);
just use the if statement to trigger a bool that then does the lerp, so the camera will move to what you need it to and not just when the player hits a certain point. Then when the camera is finished mooving, reset the bool ready for next time