Hi all I am developing an endless runner game and need help on solving an issue. My version of the endless runner is where the player moves along the z-axis while tiles spawning in-front of the player. The tiles have 3 lanes. So my issue is this. I have this spawning script which I got from researching the net and ran into the issue the script is written where the tiles moving under the player rather than player running forward. I tried to convert it so that that objects are spawned in front of the player (z-axis) but I didn't quite achieve what I intended for.
So my errors are,
objects (obstacles) are spawning behind the player and eventually stops
objects are spawned only in one lane.
So how do I how do I rectify above errors?
Here's my script.
public class Spawner : MonoBehaviour {
[Space(10)]
[Header("Obstacles:")]
public float objectSpawnWait;
public Vector3 randPos1;
public Vector3 randPos2;
public Vector3 randPos3;
public float spawnZ; //distance between player and the object
public float spawnDistance; //distance between object
public GameObject[] obstacles;
private Transform playerTranform;
void Start()
{
playerTranform = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
if(playerTranform.position.z > spawnZ * spawnDistance)
{
StartCoroutine(SpawnObstacle());
}
}
IEnumerator SpawnObstacle()
{
if (obstacles.Length > 0)
{
int randomObstacle = Random.Range(0, obstacles.Length);
GameObject obstacle = Instantiate(obstacles[randomObstacle]);
obstacle.transform.SetParent(transform);
MoveObjectsToTheFront(obstacle);
}
yield return new WaitForSeconds(5);
}
private void MoveObjectsToTheFront(GameObject moveObject)
{
int randomPosNum = Random.Range(0, 2);
Vector3 randomPosition;
if (randomPosNum == 0)
randomPosition = randPos1; // Vector3(2, 0.5, 5)
else if (randomPosNum == 1)
randomPosition = randPos2; // Vector3(0, 0.5, 5)
else
randomPosition = randPos3; // Vector3(-2, 0.5, 5)
moveObject.transform.position = randomPosition * spawnZ;
spawnZ += spawnDistance;
}
}
Any help would be greatly appreciated. Thanks.
Related
I made a Gun Script (using YT videos) and a Bullethole GameObject should Instantiate at the Position the RayCast hits an Object. But it is always at 0 0 0. I tried many thing but non work.
GameObject bulletHoleClone = Instantiate(bulletHole, rayHit.point, Quaternion.Euler(0, 180, 0));
Destroy(bulletHoleClone, bulletHoleTime);
Whole Code:
using TMPro;
using UnityEngine;
public class GunScript : MonoBehaviour
{
//Gun Stats
public int damage;
public float timeBetweenShooting, spread, range, reloadTime, timeBetweenShots;
public int magazineSize, bulletsPerTap;
public bool allowHoldButton;
int bulletsLeft, bulletsShot;
private float normalSpread;
//bools
bool shooting, readyToShoot, reloading;
//Graphics
public CameraShake camShake;
public float camShakeStrengh, camShakeDuration;
public GameObject muzzleFlash, bulletHole;
public TextMeshProUGUI text;
public float muzzleFlashTime = 0.1f; //Will be deleted after there is an actual Effect and not a Box
public float bulletHoleTime = 3f;
//Refrences
public Camera fpsCam;
public Transform attackPoint;
private RaycastHit rayHit;
public LayerMask dafuqIsEnemey;
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
bulletsLeft = magazineSize;
readyToShoot = true;
normalSpread = spread;
}
private void Update()
{
MyInput();
//Set Text to BulletAmount
text.SetText(bulletsLeft + " / " + magazineSize);
}
private void MyInput()
{
if (allowHoldButton) shooting = Input.GetKey(KeyCode.Mouse0);
else shooting = Input.GetKeyDown(KeyCode.Mouse0);
if (Input.GetKeyDown(KeyCode.R) && bulletsLeft < magazineSize && !reloading) Reload();
//PewPew
if (readyToShoot && shooting && !reloading && bulletsLeft > 0)
{
bulletsShot = bulletsPerTap;
Shoot();
}
if (rb.velocity.magnitude > 0)
spread = normalSpread * 1.5f;
else spread = normalSpread;
if (Input.GetKey(KeyCode.LeftControl))
spread = normalSpread * 2f;
else spread = normalSpread;
}
private void Shoot()
{
readyToShoot = false;
//Bullets go BRRRRRRRRRR
float x = Random.Range(-spread, spread);
float y = Random.Range(-spread, spread);
//Calculate Direction with Spread
Vector3 direction = fpsCam.transform.forward + new Vector3(x, y, 0);
//Maybe Add that the Ray Isnt coming out of your Head. Instead make it apear on the GunTip
if (Physics.Raycast(fpsCam.transform.position, direction, out rayHit, range, dafuqIsEnemey))
{
Debug.Log(rayHit.collider.name);
if (rayHit.collider.CompareTag("Enemy"))
{
Debug.Log("Hit An Enemy");
//rayHit.collider.GetComponent<ShootingAi>().TakeDamage(damage);
}
}
//Shake Camera
camShake.Shake(camShakeDuration, camShakeStrengh);
//Graphics
GameObject bulletHoleClone = Instantiate(bulletHole, rayHit.point, Quaternion.Euler(0, 180, 0));
GameObject muzzleClone = Instantiate(muzzleFlash, attackPoint.position, Quaternion.identity);
Destroy(muzzleClone, muzzleFlashTime);
Destroy(bulletHoleClone, bulletHoleTime);
Debug.Log(rayHit.point);
bulletsLeft--;
bulletsShot--;
Invoke("ResetShot", timeBetweenShooting);
if (bulletsShot > 0 && bulletsLeft > 0)
Invoke("Shoot", timeBetweenShooting);
}
private void ResetShot()
{
readyToShoot = true;
}
private void Reload()
{
reloading = true;
Invoke("ReloadFinished", reloadTime);
}
private void ReloadFinished()
{
bulletsLeft = magazineSize;
reloading = false;
}
}
You're not hitting anything. You check to see if you hit something, and then spawn a bullet hole even if you didn't hit anything.
If you didn't hit anything, there isn't anywhere to put a bullet hole.
The problem is that you're using the raycast hit as a local variable, which allows you to use it outside of the scope where it's actually valid.
Delete rayHit from your class and define it in the raycast instead, like
out RaycastHit rayHit
then it should only be scoped for the if statement and you'll get a compiler error anywhere it's not valid.
This brings up a larger question about what do you do if it misses an enemy, or which layers you should be raycasting against. Personally I think you'd want to raycast against everything, then you can try to determine what you hit and what that means later. Only other really good alternative would be to raycast against enemies, then repeat that against objects if that misses, but it's a lot of overhead to keep spinning up the raycasting engine. You'd be better off firing against multiple layers and getting the layer off the collider.
PS too your spread is probably way, way too big. For values of spread, 0.01 is half a degree, 0.1 is five degrees, 1 is 45 degrees.
I found the Problem. Just tell the Gun that the Layer "Ground" is also an Enemy. Now it works. Thanks
Basically you need to Transform Direction e.g camTrans.TransformDirection. You can try this way
public class Raytest : MonoBehaviour
{
public Camera fpsCam;
public float maxDistance = 200f;
public bool update = true;
public GameObject objectToSpawn;
private void Update()
{
if(!update) return;
var camTrans = fpsCam.transform;
if (Physics.Raycast(camTrans.position, camTrans.TransformDirection(Vector3.forward), out var hit, maxDistance))
{
Debug.DrawRay(camTrans.position, camTrans.TransformDirection(Vector3.forward) * hit.distance, Color.red);
Debug.Log("Did Hit");
var clone = Instantiate(objectToSpawn, hit.point, Quaternion.Euler(0, 180, 0));
update = false;
}
else
{
Debug.DrawRay(camTrans.position, camTrans.TransformDirection(Vector3.forward) * 1000, Color.white);
Debug.Log("Did not Hit");
}
}
}
After hitting raycast
I have almost finished my first game in Unity, which is a simple maze with characters collecting different coins. As it was a hassle to manually put all the different coins in the terrain, I decided to try and make a random generator for the coins.
The script is largely working, but the problem is that it's spawning objects most of the time inside walls. So I tried to add rigid bodies to coins, but this is messing with the coins position as the coins have a script for rotation. I also have the coins collision triggered because they perform a certain action when colliding with the character.
So after some research I saw that I need to check in my generator script for a near object collision and somehow change the spawn position of the coin if it's true. I'm trying to figure out how I can do that. My script is attached to my terrain and it looks like this:
using System.Collections;
using CustomArrayExtensions;
using UnityEngine;
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while (amount < limit)
{
Instantiate(selectedObject, new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f)), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.name == "Fox_Main")
{
Debug.Log("Collision Detected");
}
}
}
So i tried both answers and they didn't help me.Coins and other objects is still spawning close/inside the walls.My boxes have box collider and rigidbody with all positions/rotations locked(i need it that way so that i can destroy it later without moving it with the character),walls have box collider.So i tried to modify your solutions following Unity Documentation.I failed too.Here is what i have done so far.
See here
using System.Collections;
using UnityEngine;
public class RandomCrates : MonoBehaviour
{
public GameObject[] crates;
private GameObject selectedCrate;
public int limit = 0;
public float x, y, z;
public float forX_From,forX_To,forZ_From,forZ_To;
private int mask = 1 << 7;
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
for (int i = 0; i < crates.Length; i++)
{
for (int j = 0; j < limit; j++)
{
Vector3 freePos = GetFreePosition();
Instantiate(crates[i], freePos, Quaternion.identity);
yield return new WaitForSeconds(0.0f);
}
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(forX_From, forX_To), 10.0f, Random.Range(forZ_From, forZ_To));
} while (Physics.OverlapBoxNonAlloc(position, new Vector3(x, y, z), collisions, Quaternion.identity, mask) > 0);
return position;
}
}
Unity provides a simple way to detect collisions in specific position with Physics.OverlapSphere:
public class ObjectSpawner : MonoBehaviour
{
[SerializeField] public GameObject[] letters;
GameObject selectedObject;
private int amount = 0;
public int limit;
private float radius = 2f; //Radius of object to spawn
void Start()
{
StartCoroutine(SpawnObjects());
}
IEnumerator SpawnObjects()
{
selectedObject = letters[Random.Range(0, letters.Length)];
while (amount < limit)
{
Vector3 spawnPos = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
//Check collisions
if (DetectCollisions(spawnPos) > 0)
continue;
Instantiate(selectedObject, spawnPos, Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
private int DetectCollisions(Vector3 pos)
{
Collider[] hitColliders = Physics.OverlapSphere(pos, radius);
return hitColliders.Length;
}
}
In this way, if there is a collision in spawn position, the coin will not be spawned.
There are different ways of approaching this problem and given more information one could come up with a smarter approach. However, to provide you with a simple solution to your problem: You could just pick random positions until you find a "free spot".
Assuming your coins to have a size of 1, you could do something like:
IEnumerator SpawnObjects()
{
selectedObject = letters.GetRandom();
while(amount < limit)
{
Vector3 freePos = GetFreePosition();
Instantiate(selectedObject, freePos), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
amount++;
}
}
Vector3 GetFreePosition()
{
Vector3 position;
Collider[] collisions = new Collider[1];
do
{
position = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
}
while(Physics.OverlapSphereNonAlloc(position, 1f, collisions) > 0);
return position;
}
Please note that Physics.OverlapSphereNonAlloc() can also accept a LayerMask for filtering collisions based on layers. You might want to look into this, to prevent detecting collision with your terrain, which may well keep the do-while loop from exiting and freeze Unity.
I have a script that spawns my ground prefabs infinitely. The only problem is that occasionally there is a "bump" for the player to hit at the boundary of a new ground prefab.
It seems like one prefab is slightly higher than the previous one and when the player hits that small bump they go flying. How do I fix this?
I might just make the player a game object instead of a rigidbody and animate it instead of using actual physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundManager : MonoBehaviour
{
//oH is object height
public GameObject[] ground;
public GameObject[] obstacles;
private Transform playerTransform;
private float spawnZ = 0.0f;
private float spawnO = 0.0f;
public float oH;
private float tileLength = 40.0f;
private float oLength = 36.0f;
private int tilesOnScreen = 8;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < tilesOnScreen; i++)
{
SpawnTile();
SpawnObstacle();
}
}
// Player must be tagged "Player"
void Update()
{
if (playerTransform.position.z > (spawnZ - tilesOnScreen * tileLength))
{
SpawnTile();
SpawnObstacle();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
go = Instantiate(ground[0]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private void SpawnObstacle()
{
GameObject go;
go = Instantiate(obstacles[Random.Range(0, obstacles.Length)]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = new Vector3(0, oH, 1 * spawnO);
spawnO += oLength;
}
}
This code works to infinitely spawn the ground objects but has the bumps that I described. Just one bump is enough to screw up the whole game.
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 making a 'run' game in Unity and I'm making a prototype with a ball, that has other balls following him. If the followers hit an object they get destroyed after some time. To make it so you don't run out of enemies I made a trigger that spawns new enemies. In the code this is the function Addzombies.
How do I make them spawn not on the same point, if i run it now they
start on eachother and bounce around as an explosion.
How do i make some start in the air, i tried it but they don't
spawn.
My code:
using UnityEngine;
using System.Collections;
public class Ball : MonoBehaviour {
public float InputForce;
public GUIText guiText;
public float rotationHorizontal;
public AudioClip ACeffect2;
public GameObject zombiePrefab;
void FixedUpdate() {
rigidbody.AddForce( Camera.main.transform.right * Input.GetAxis("Horizontal") * InputForce);
rigidbody.AddForce( Camera.main.transform.forward * Input.GetAxis("Vertical") * InputForce);
transform.position += Vector3.forward *InputForce * Time.deltaTime;
rotationHorizontal = Input.GetAxis("Horizontal") * InputForce;
rotationHorizontal *= Time.deltaTime;
rigidbody.AddRelativeTorque (Vector3.back * rotationHorizontal);
}
void OnCollisionEnter(Collision col){
if (col.gameObject.name == "Zombie") {
Debug.Log ("Player geraakt, nu ben je eigenlijk dood");
}
if (col.gameObject.name == "Obstakel1") {
Debug.Log ("Obstakel1 geraakt");
audio.PlayOneShot(ACeffect2);
InputForce = 0;
}
if (col.gameObject.name == "Obstakel2") {
Debug.Log ("Obstakel2 geraakt");
}
}
void AddZombies(int aantal){
for (int i = 0; i < aantal; i++){
GameObject go = GameObject.Instantiate(zombiePrefab, transform.position - new Vector3(0, 0, 7 + i),Quaternion.identity) as GameObject;
Zombie zb = go.GetComponent<Zombie>();
zb.target = gameObject.transform;
}
}
void OnTriggerEnter(Collider col) {
Debug.Log ("Enter" +col.name);
if (col.tag == "AddZombies"){
AddZombies(4);
}
}
void OnTriggerExit(Collider col) {
Debug.Log ("Leaving with" +col.name);
}
}
I will advice on how things could be done, but you will have to make changes to make it suit your requirement
public Transform zombiePrefab; // From the editor drag and drop your prefab
void addZombies()
{
// as you need them to be not on the same point
int randomX = Random.Range(-10.0F, 10.0F);
for (int i = 0; i < aantal; i++){
// make a transform
var zombieTransform = Instantiate(zombiePrefab) as Transform;
zombieTransform.position = new Vector3(randomX, 0, 7 + i);
transform.GetComponent<Rigidbody>().enabled = false;
// make it enable when you need and add force to make them fall
}
}
I would suggest passing the number of zombies to pass, and an integer representing the range of space that they can spawn in. Then just use UnityEngine.Random with said integer for each zombie to produce several different co-ordinates for spawning the zombies.
As for getting them to spawn in the air, just increase the y co-ordinate when you instantiate the zombie.