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.
Related
Im throwing a bomb using physics made by code. For some reason it doesnt detect collision unless my physics stop applying force to the object. in order to bypass it I cancelled the applied force and applied gravity to it on collision enter and put the movement of the bomb to LateUpdate so it will trigger after the OnCollisionEnter but the bomb collides only most of the time with the floor (mesh collision, the floor made with ProBuilder) and not all of the time. the bomb collision detection is set to continuous
Will appreciate all the help, Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BombBehavior : MonoBehaviour
{
[SerializeField] float ExplosionForce = 300;
[SerializeField] float ExplosionRadius;
[SerializeField] float LaunchForceX;
[SerializeField] float LaunchForceY;
[SerializeField] float Delay;
float countdown;
Rigidbody rigidbodyy;
float gravity = 1;
Player player;
bool HasExploded = false;
// Start is called before the first frame update
void Start()
{
rigidbodyy = GetComponent<Rigidbody>();
player = Player.p;
GetLaunchForceX();
countdown = Delay;
}
private void GetLaunchForceX()
{
if (transform.position.x > player.transform.position.x)
{
LaunchForceX *= 1;
}
else if (transform.position.x < player.transform.position.x)
{
LaunchForceX *= -1;
}
}
private void LateUpdate()
{
ThrowBomb();
}
private void Update()
{
countdown -= Time.deltaTime;
if (countdown <= 0 && !HasExploded)
{
ExplodeNearEnemy();
}
}
private void ThrowBomb()
{
if (rigidbodyy.useGravity == false)
{
Vector3 ThrowDirection = new Vector3(LaunchForceX, LaunchForceY, 0);
LaunchForceY -= gravity;
ThrowDirection.y = LaunchForceY;
transform.Translate(ThrowDirection * Time.deltaTime);
}
}
private void ExplodeNearEnemy()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, ExplosionRadius);
foreach (Collider collider in colliders)
{
if (collider.gameObject.CompareTag("Enemy"))
{
Rigidbody enemyRB = collider.GetComponent<Rigidbody>();
if (enemyRB != null)
{
enemyRB.useGravity = true;
enemyRB.AddExplosionForce(ExplosionForce, transform.position, ExplosionRadius);
Destroy(enemyRB.gameObject,1);
}
}
}
//Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
LaunchForceY = 0;
LaunchForceX = 0;
gravity = 0;
rigidbodyy.useGravity = true;
}
}
I don't exactly understand the problem but it looks like you want to make a Bomb/Granate so why dont you just write a function and use AddForce()?
void Thow(Vector3 direction, float strength)
{
rigidbodyy.AddForce(direction * strength, ForceMode.Impulse);
}
something like this should help as you only need to get the throwing direction and apply a strength then Unity will handle the rest
Or maybe if your bomb doesn't collide with the ground give the ground a rigidbody and set kinematic to true
Use the MovePosition() to move a rigid body if you want colliders etc to work. Teleporting a rigid body by altering the transform directly messes up the physics.
I'm building a team game (dungeon crawler) and I was faced with a problem.
If we put the player prefab directly in the sceane the enemies treat the player correctly (follow, attack, etc). The problem is: my peer created a player spawn pad that instantiate the exacly same prefab but the enemies ignore completly the player.
Can someone help me?
Here's the enemy script:
public bool isDead = false;
public float speed = 4f;
public int hp = 0;
public int dmg = 0;
public int def = 0;
public int amtSouls = 0;
//Item drop
public GameObject drop;
//follow player
public GameObject Player;
private NavMeshAgent Enemy;
public float enemyDistanceRun = 5.0f; //radius to spot the player
//--
Rigidbody rb;
Scene currentScene;
PlayerHealthScript phs;
public Type type; //defines the type (common, elite, boss)
public enum Type
{
Common,
Elite,
Boss
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
//not working
hp -= phs.dmg;
}
}
void death()
{
isDead = true;
Instantiate(drop, transform.position, drop.transform.rotation);
//---- get Destroy script reference
GameObject ds = GameObject.Find("Soul");
Destroy des = ds.GetComponent<Destroy>();
//----
Destroy(gameObject);
//send the amount of souls to the Destroy script
des.sls += amtSouls;
}
void Start()
{
rb = this.GetComponent<Rigidbody>();
Enemy = GetComponent<NavMeshAgent>();
phs = GameObject.Find("Player Character").GetComponent<PlayerHealthScript>();
currentScene = SceneManager.GetActiveScene();
// gate.SetActive(false);
if (CompareTag(Type.Common.ToString()))
{
hp = 60;
dmg = 5;
def = 5;
amtSouls = 10;
// gate.SetActive(true);
}
else
if(CompareTag(Type.Elite.ToString()))
{
hp = 100;
dmg = 15;
def = 10;
amtSouls = 25;
}
else
if(CompareTag(Type.Boss.ToString()))
{
hp = 200;
dmg = 25;
def = 20;
amtSouls = 50;
}
// Retrieve the index of the scene in the project's build settings.
int buildIndex = currentScene.buildIndex;
// Check the scene and multiply the values
switch (buildIndex)
{
case 0:
//Do nothing because values already setted
break;
case 1:
hp = hp * 2;
dmg = dmg * 2;
def = def * 2;
amtSouls = amtSouls + 2;
break;
}
}
void Update()
{
float distance = Vector3.Distance(transform.position, Player.transform.position);
//chase player
if(distance < enemyDistanceRun)
{
Vector3 dirToPlayer = transform.position - Player.transform.position;
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
}
if (hp <= 0)
{
death();
}
Player Spawn Pad Script:
public GameObject PlayerPrefab;
public GameObject MainCam;
public GameObject FreelookCam;
public GameObject PlayerHUD;
// Start is called before the first frame update
void Start()
{
GameObject Player = Instantiate(PlayerPrefab, transform.localPosition, transform.localRotation);
Player.name = PlayerPrefab.name;
GameObject MainCam1 = Instantiate(MainCam, transform.localPosition, transform.localRotation);
MainCam1.name = MainCam.name;
GameObject FreeCam = Instantiate(FreelookCam, transform.localPosition, transform.localRotation);
FreeCam.name = FreelookCam.name;
GameObject HUD = Instantiate(PlayerHUD, transform.localPosition, transform.localRotation);
HUD.name = PlayerHUD.name;
GameObject.Destroy(gameObject);
}
// Update is called once per frame
void Update()
{
}
As it was said in a comment, your enemy never checks if a new Player is spawned.
"The best solution" depends on how often you are going to spawn and destroy that player GameObject.
For example, if you're only going to spawn the player at the beginning of the scene, then you could alter the scripts' execution order so that the player is spawned before the enemy tries to look for it.
[DefaultExecutionOrder(-50)]
class PlayerSpawnPad : MonoBehaviour { /* ... */ }
And in the enemy class you get the reference in one of these three ways:
player = GameObject.Find("Player Character");
// Not really recommended. If you decide to change the game object's name
// later in the project, you'd have to change this too.
player = FindObjectWithTag("Player");
// Better, but although tags are less likely to be changed, it's still
// something more to think about
player = FindObjectOfType<Player>();
// If you ever decide to change the player script's name, the compiler will
// tell you immediately
If instead you plan on spawning the player after the enemy, you could use an event
using System;
class Player : MonoBehaviour {
public static event Action<Player> onPlayerSpawned;
private void Start() {
onPlayerSpawned(this);
}
/* ... */
}
class Enemy : MonoBehaviour {
private GameObject player;
private void Start() {
Player.onPlayerSpawned += RegisterPlayer;
}
private void RegisterPlayer(Player player) {
this.player = player.gameObject;
}
/* ... */
}
I found out the solution.
{
Vector3 dirToPlayer = transform.position - Player.transform.position;///<-
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
}
if (distance < enemyDistanceRun)
{
Vector3 dirToPlayer = transform.position - phs.transform.position;//<-
Vector3 newPos = transform.position - dirToPlayer;
Enemy.SetDestination(newPos);
Debug.Log("Chacing.");
}
The enemy was following the Player gameobject that, for some reason didn't recognize so I put it to follow the player health script holder and works perfectly.
I am trying to develop a 2d game where my enemies will pop-up and disappear in random position without overlapping with each other. I am following this tutorial in youtube https://www.youtube.com/watch?v=iLTP4EbM1N4 but when my player touch a enemy then he is losing 2 lives instead of 1. I think it's because enemies randomly spawn in the same spawnpoint (I have 5 enemies). Do you have any suggestion how to fix that? Any help would be highly appreciated. Thanks in advance. Here is my two scripts:
SpawnItems.cs
using UnityEngine;
using System.Collections;
public class SpawnItems : MonoBehaviour
{
public Transform[] SpawnPoints;
public float spawnTime = .5f;
public GameObject[] Coins;
void Start ()
{
InvokeRepeating ("SpawnCoins", spawnTime, spawnTime);
}
void Update ()
{
}
void SpawnCoins()
{
for (int i = 0; i < Coins.Length; i++) {
int spawnIndex = Random.Range (0, SpawnPoints.Length);
//int objectIndex = Random.Range (0, Coins.Length);
Instantiate (Coins [i], SpawnPoints [spawnIndex].position, SpawnPoints [spawnIndex].rotation);
}
}
Destroy.cs
using UnityEngine;
using System.Collections;
public class DestoryScript: MonoBehaviour
{
public float destoryTime = .5f;
private float rotateSpeed = 300.0f;
void Start ()
{
Destroy (gameObject, destoryTime);
}
void Update ()
{
transform.Rotate (Vector3.forward * Time.deltaTime * rotateSpeed);
}
}
Orange ball overlaps with green ball
Your SpawnCoins() in SpawnItems.cs should be like:
void SpawnCoins()
{
Transform currentSpawnPoint = SpawnPoints[Random.Range(0, SpawnPoints.Length)].transform;
Instantiate (Coins [Random.Range(0, Coins.Length)], currentSpawnPoint.position, currentSpawnPoint.rotation);
}
I am using EnemyScript to move the enemy towards the player and killing the player, but I'm unable to spawn it randomly in code. I am currently spawning it directly through screen by placing the prefab on the scene.
Here is my EnemyScript
using UnityEngine;
using System.Collections;
public class EnemyScript : MonoBehaviour {
public Transform target;
public float speed = 2f;
void Update ()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
void OnTriggerEnter2D(Collider2D otherCollider)
{
PlayerControl shot = otherCollider.gameObject.GetComponent<PlayerControl>();
if (shot != null)
{
Destroy(shot.gameObject);
}
}
}
you could use something similar to this:
public GameObject myObj;
void Start ()
{
enemy = GameObject.Find("enemy");
InvokeRepeating("SpawnEnemy", 1.6F, 1F);
}
public void SpawnEnemy()
{
Vector3 position = new Vector3(Random.Range(35.0F, 40.0F), Random.Range(-4F, 2F), 0);
Instantiate(myObj, position, Quaternion.identity);
}
in the InvokeRepeating call you could possibly add the random range there also instead of a timed instantiate. This example is just a snippet of some prototype code i did a while ago, it may not suit your needs directly but hopefully will give you a general idea on what to do.
EDIT: to make sense, put this into a blank object somewhere in your scene, dont attach this to the actual enemy.
i programmed it like this.
public GameObject enemyPrefab;
public float numEnemies;
public float xMin = 20F;
public float xMax = 85F;
public float yMin = 3.5F;
public float yMax = -4.5F;
void Start () {
for (int i=0; i< numEnemies; i++) {
Vector3 newPos = new Vector3(Random.Range(xMin,xMax),Random.Range(yMin,yMax),0);
GameObject enemy = Instantiate(enemyPrefab,newPos,Quaternion.identity) as GameObject;
}
}
This will spawn a random number of enemies, at random locations, after random periods of time, all adjustable in the Inspector.
public float minTime = 1;
public float maxTime = 3;
public int minSpawn = 1;
public int maxSpawn = 4;
public Bounds spawnArea; //set these bounds in the inspector
public GameObject enemyPrefab;
private spawnTimer = 0;
Vector3 randomWithinBounds(Bounds r) {
return new Vector3(
Random.Range(r.min.x, r.max.x),
Random.Range(r.min.y, r.max.y),
Random.Range(r.min.z, r.max.z));
}
void Update() {
spawnTimer -= Time.deltaTime;
if(spawnTimer <= 0) {
spawnTimer += Random.Range(minTime, maxTime);
int randomSpawnCount = Random.Range(minSpawn, maxSpawn);
for(int i = 0; i < randomSpawnCount; i++) {
Instantiate(transform.transformPoint(enemyPrefab), randomWithinBounds(spawnArea), Quaternion.identity);
}
}
}
//bonus: this will show you the spawn area in the editor
void OnDrawGizmos() {
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.color = Color.yellow;
Gizmos.drawWireCube(spawnArea.center, spawnArea.size);
}
People on here seem to like using coroutines for time delays, but I personally prefer to track my own timers in Update() to maximize control and predictability.
I have this issue and I can't fix it. I've already tried to search on internet but I found nothing.
The "projectile spawn" is situated next to my character. When I left-click on my mouse, the projectile starts shooting going forward (in the Z-axis at 20 speed, for example) but there is this issue that is making me mad. Sometimes it starts from the right place, sometimes under and under every click.
public Rigidbody projectile;
public float speed = 20;
private Transform spawnPoint;
private int SPAWN_DISTANCE = 1;
void Awake()
{
spawnPoint = transform.Find("SpawnPoint");
}
void Update ()
{
//
if (Input.GetButtonDown("Fire1"))
{
atkSpeed = 0.2f;
Shoot(projectile);
attackTime = atkSpeed;
chargeLevel = 0;
}
//
}
void Shoot(Rigidbody proj){
Rigidbody _projBody = Instantiate(proj, spawnPoint.position + SPAWN_DISTANCE * transform.forward, transform.rotation) as Rigidbody;
_projBody.velocity = transform.TransformDirection(new Vector3(0, 0,speed));
}
Video of what is happening: https://www.youtube.com/watch?v=GLgrfl2zU9w
EDIT: I SOLVED MYSELF YESTERDAY.
Put an Empty GameObject where you want the projectile to spawn from. Nest it inside the character that fires.
Then find the SpawnPoint.
private Transform spawnPoint;
private int SPAWN_DISTANCE = 1;
void Awake()
{
spawnPoint = transform.Find("SpawnPoint");
}
Also make this change to your shooting.
void Shoot()
{
Rigidbody _projBody = Instantiate(projectile, spawnPoint.position + SPAWN_DISTANCE * transform.forward, transform.rotation) as Rigidbody;
_projBody.velocity = transform.TransformDirection(new Vector3(0, 0,speed));
}