I am trying to make an RTS game using Photon Networking For Unity. What I am trying to do is depending on the players in the specific room (2 in this example) all the players spawn at a different location. What I am having trouble is making a system that spawns the players at different locations and not at the same place when the game starts and PhotonNetwork.Instantiate is called. How can I use SpawnPositions With A list to check if a player is already spawned at the given location if so then it spawns at the next one I hope I covered everything if you need to know anything else just ask.
private GameObject playerPrefab; // Players Prefab
[SerializeField]
public Transform[] SpawnPositions; // 2 Spawn Positions in The unity hierarchy
public List<Vector3> spawnPoints = new List<Vector3>(); // List of spawnpoints to add to
void Start()
{
PhotonNetwork.Instantiate(playerPrefab.name, [what to do here], Quaternion.Euler(0, 0, 0));
}
Edit 2: using Transform[] SpawnPositions
private int spawnIndex;
...
void CreatePlayerObject()
{
if(spawnIndex >= SpawnPositions.length) spawnIndex = 0;
Vector3 position = SpawnPositions [spawnIndex].transform.position;
GameObject newPlayerObject = Instantiate(player, position, Quaternion.identity);
spawnIndex++;
}
...
end edit 2
You can make spawn points. I made the spawn points automatically find the right height. If you don't need auto height, you can use an empty MonoBehaviour or even use a tag. You can make many GameObjects in different positions with this script on them.
...
public class spawnPoint : MonoBehaviour {
public bool autoHeight = true;
public LayerMask putOnTopOfThese;
// Use this for initialization
void Start () {
RaycastHit hit;
Physics.Raycast (transform.position + Vector3.up * 100, -Vector3.up, out hit, 200, putOnTopOfThese);
transform.position = hit.point + Vector3.up;
}
}
To make every player spawn in a different position, you can cycle through the spawn points. Keep in mind that if you use a tag for spawn points instead of a MonoBehaviour, you would need to use GameObject.FindObjectsWithTag instead of FindObjectsOfType
For spawning in different positions:
private int spawnIndex;
...
void CreatePlayerObject()
{
spawnPoint[] sp = GameObject.FindObjectsOfType<spawnPoint> ();
if(spawnIndex >= sp.length) spawnIndex = 0;
Vector3 position = sp [spawnIndex].transform.position;
GameObject newPlayerObject = Instantiate(player, position, Quaternion.identity);
spawnIndex++;
}
...
For random spawning:
...
void CreatePlayerObject()
{
spawnPoint[] sp = GameObject.FindObjectsOfType<spawnPoint> ();
int chosen = Random.Range (0, sp.Length);
Vector3 position = sp [chosen].transform.position;
GameObject newPlayerObject = Instantiate(player, position, Quaternion.identity);
}
...
Edit: clarity
Thank you bazilbaachu(https://answers.unity.com/users/1127669/bazilbaachu.html).
I am posting my entire workflow of instantiating players at different positions:
Initially checking whether we are master client or not:
Then obtain list of players and send rpc to each of them individually on positions to spawn them.You can store an array of positions and send index in rpc.
Here is the code:
photonView = PhotonView.Get(this);
if(PhotonNetwork.IsMasterClient)
{
foreach(Player pl in PhotonNetwork.PlayerList)
{
//Debug.Log(pl);
photonView.RPC("InstantiationPlayer",pl,index);
index++;
}
}
[PunRPC]
void InstantiationPlayer(int index)
{
PhotonNetwork.Instantiate(Playerprefabs[value].name,SpawnPoints[index].transform.position, Quaternion.identity, 0);
}
Thank you.
public Transform[] spawnPoints;
private void Start()
{
int randomNumber = Random.Range(0, spawnPoints.Length);
Transform spawnPoint = spawnPoints[randomNumber];
PhotonNetwork.Instantiate(playerPrefabs.name, spawnPoint.position, Quaternion.identity);
}
Drag your GameObject points into Spawn Points
Related
Im working on bomb system in Unity 3D. I have uploaded some explosion effects from Unity Asset Store and I want implement them into my project. I want to plant bomb with key "K" then wait 3sec to detonate it with explosion effect and give some damage to nearby objects. The problem is that explosion appears in such different position as it should. In my opinion this is Editor problem , the code looks fine. I will give you some screenshoots(https://drive.google.com/file/d/19Yzymch9RdTa-E6RkbvvyfzMWjMJHo52/view?usp=sharing)and my bomb script :
public class BoombScript : MonoBehaviour
{
public GameObject boombEffect;
[SerializeField]
private float radius;
[SerializeField]
private float force;
[SerializeField]
private int explosiveDamage;
public void Explode()
{
Instantiate(boombEffect, transform.position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + transform.position);
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, transform.position, radius);
}
}
}
public IEnumerator WaitForExplode()
{
yield return new WaitForSeconds(3f);
Explode();
}
}
In my opinion this is Editor problem , the code looks fine.
In general I would always doubt such a statement.
I can only guess but if I understand you correctly you rather want to use the position of your object the moment when you start the Coroutine, not the one after 3 seconds:
// instead of using the current "reansform.position" rather
// use the pre-stored position passed in via parameter
public void Explode(Vector3 position)
{
Instantiate(boombEffect, position, Quaternion.identity);
Debug.Log("Transform" + transform);
Debug.Log("Position" + position);
Collider[] colliders = Physics.OverlapSphere(position, radius);
foreach(Collider rangedObject in colliders)
{
GateScript Gate = rangedObject.GetComponent<GateScript>();
Rigidbody rb = rangedObject.GetComponent<Rigidbody>();
if(Gate != null)
{
Gate.GateDestroy(explosiveDamage);
}
if(rb != null)
{
rb.AddExplosionForce(force, position, radius);
}
}
}
// If really needed you could still also have an overload
// e.g. if at some places you actually call this method from the outside
// immediately
public void Explode()
{
Explode(transform.position);
}
public IEnumerator WaitForExplode()
{
// Store the position when the routine is started
var position = transform.position;
yield return new WaitForSeconds(3f);
// Instead of using the current position rather pass in
// the previously stored one of when the routine was started
Explode(position);
}
I want to respawn object at a new position immediately after player collides with it.
For now my code just spawn the object after respawn time and destroy the previous object.
The code on my empty object on unity2d is
public GameObject point;
private Vector2 screenBounds;
public float respawnTime = 10f;
GameObject star;
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
spawnStar();
StartCoroutine(starWave());
}
public void spawnStar()
{
Debug.Log("Yeah it works");
star = Instantiate(point) as GameObject;
star.transform.position = new Vector2(Random.Range(-screenBounds.x, screenBounds.x), Random.Range(-screenBounds.y, screenBounds.y));
}
IEnumerator starWave()
{
while (true)
{
yield return new WaitForSeconds(respawnTime);
Destroy(star);
spawnStar();
}
}
And the script on object prefab is
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == "Player")
{
Debug.Log("this is star collider destory");
Destroy(this.gameObject);
}
}
Spawner script
public GameObject point;
void Start()
{
spawnStar();
}
public void spawnStar()
{
Debug.Log("Yeah it works");
star = Instantiate(point) as GameObject;
}
Prefab script
// how long before our star moves
private timeBeforeMoving = 10f;
// our current time
private currentTimer = 0.0f;
// the screen size
private Vector2 screenBounds;
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
transform.position = GenerateNewPosition();
}
private void Update()
{
// increase the time that has passed
currentTimer += Time.deltaTime;
// we have reached the timer threshold, so move the star
if(currentTimer >= timeBeforeMoving)
{
MoveOurStar();
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.tag == "Player")
{
MoveOurStar();
}
}
private void MoveOurStar()
{
// reset the timer as we just moved
currentTimer = 0.0f;
transform.position = GenerateNewPosition();
}
private Vector2 GenerateNewPosition()
{
return new Vector2(Random.Range(-screenBounds.x, screenBounds.x), Random.Range(-screenBounds.y, screenBounds.y));;
}
Instead of destroying it run your randomize position code again. If you do not want it spawning in the same location as the player, you can use a circle cast to determine if the new location the object will spawn is still on top of the player. If it is, just randomly pick a new location again.
If you do not want to put your spawn code inside of the star script when you create the star you can set a delegate callback in the star script to call back to the script that is spawning it letting it know it needs to be moved. Or make the spawner a singleton or even implement an eventsystem to fire an event when the object is destroyed letting the manager know to move that specific object.
If you would like to just go with the example code snippet I provided and are having trouble implementing the circle cast, let me know and I can provide another snippet.
Edit: I changed the code around a bit. Now the spawner will just spawn a single star and the star handles where it moves after it collides.
In unity 3d, I've create an Empty GameObject that Instanciates a simple 3d model when i click with the mouse ( 3d model imported from blender).
I've written a simple script that moves the EmptyGameObject using
transform.Translate(speed * Time.deltaTime, 0, 0);
But when i run the project, only the Empty Game Object moving but I need that the 3d model moving to x asix. How can i resolve this problem? Thanks for the help.
Moving script (putting on EmptyGameObject):
public float speed = 0f;
// Start is called before the first frame update
void Start()
{
speed = 0f;
}
// Update is called once per frame
void Update()
{
transform.Translate(speed * Time.deltaTime, 0, 0);
if (Input.GetMouseButtonDown(0)) {
speed += speed + 0.5f;
}
}
Script for instantiated objects:
public GameObject model;
public GameObject model1;
public int counter = 0;
void Start(){
model= Instantiate(model, transform.position, Quaternion.identity); //spawn model
}
void Update(){
if (Input.GetMouseButtonDown(0)) {
ChangeModel();
}
}
void ChangeModel(){
counter++;
switch (counter) {
case 1:
Destroy(model);
Destroy(model1);
model = (GameObject) Instantiate(model, transform.position, Quaternion.identity);
break;
case 2:
Destroy(model);
model1= (GameObject) Instantiate(model1, transform.position, Quaternion.identity);
counter =0;
break;
}
}
In general all you need to do is spawn the new objects always as children of this EmptyGameObject. Instantiate therefore has an overload taking an additional parent Transform reference to spawn the prefab as child to.
As I understand the second script is also attached to the EmptyGameObject so your target parent is simply transform (the Transform component of the GameObject this script is attached to):
//spawn model as child of given Transform
model = Instantiate(prefab, transform.position, Quaternion.identity, transform);
Also note that a cast to (GameObject) is redundant since Instantiate anyway returns the type of the given prefab.
However
I do not really understand your usage of model, model1 and Instantiate and Destroy here ...
What do you destroy the model for if you want to directly respawn it anyway?
In general I think you would be way better also performance wise spawning them both once and only activate and deactivate according models:
public GameObject prefab;
public GameObject prefab1;
void Start()
{
model = Instantiate(prefab, transform.position, Quaternion.identity, transform);
model1 = Instantiate(prefab1, transform.position, Quaternion.identity, transform);
model1.SetActive(false);
}
void ChangeModel()
{
counter = (counter + 1) % 2;
model.SetActive(counter == 0);
model1.SetActive(counter == 1);
}
That's it. If you simply already place the two prefabs into the EmptyGameObject right from the beginning you don't need the Instantiate at all ...
And btw in case you only have two states anyway instead of a counter you could also use a simple bool here:
bool isModel1;
private void ChangeModel()
{
isModel1 = !isModel1;
model.SetActive(!isModel1);
model1.SetActive(isModel1);
}
And finally note that from your movement script:
speed += speed + 0.5f;
this results in
speed = speed + speed + 0.5f;
but I guess what you rather want is
speed += 0.5f;
which results in
speed = speed + 0.5f;
So, this code just isn't responding for whatever reason. I have an enemy that I'm trying to get to face the player at all times (The enemy swoops over the player's head back and forth periodically). But other than making a flip happen whenever a timer hits 0, which doesn't really work all that well, I can't get the Flip function to work. I know the Flipper function is fine; I already tested it out and everything. I'm just not sure how to tell the enemy that when the player is to the left of it, to turn, and vice versa.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class dragoonDetection : MonoBehaviour {
private Rigidbody2D rb;
private Animator anim;
public Transform Player;
private bool facingRight = true;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
anim = GetComponent<Animator> ();
}
void Update()
{
Flip();
}
void Flip()
{
if (Player.transform.localScale.x > 0) {
transform.localScale = new Vector3 (1.69f, 1.54f, 1f);
}
if (Player.transform.localScale.x < 0) {
transform.localScale = new Vector3 (-1.69f, 1.54f, 1f);
}
}
void Flipper()
{
facingRight = !facingRight;
Vector2 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Got any ideas? I'd rather avoid using FindGameObject because it's not actually looking for the player script. It's looking for a child transform with no script attached to the player. And because I have two different player GameObjects that you can switch to anytime in the game, it wouldn't really work for me in that regard.
You will need to perform a check of some sort against the players position with the bird position if you want it to face the player at all times. A barebones method would just be to compare the x-positions of the two objects and change the scale accordingly.
void Update()
{
transform.localScale = new Vector3(getDir()*1.69f, 1.54f, 1);
}
private int getDir()
{
if (player.transform.position.x < transform.position.x)
return -1;
else
return 1;
}
You should throw some additional checks in here to keep it from updating the scale every frame when there is no change.
Hello i have a enemy spawn system and its working fine. however the enemies are overlapping on same point because i am using random.range, I have 4 points on the map and i want each enemy to pick a point randomly. Therefore i want after spawning the enemy the other enemy gets only 3 options to spawn at and not 4.
Here is my Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Spawn : MonoBehaviour {
// The enemy prefab to be spawned.
public GameObject[] enemy;
//public Transform[] spawnPoints;
public List<Transform> spawnPoints = new List<Transform>();
private float timer = 3;
int index = 0;
List <GameObject> EnemiesList = new List<GameObject>();
private int m_enemyCount = 4;
// Update is called once per frame
void Update () {
if (timer >0)
{
timer -= Time.deltaTime;
}
if (timer <= 0 )
{
if ( EnemiesList.Count == 0 )
{
Spawner();
timer = 5;
}
}
}
void Spawner ()
{
// Create an instance of the enemy prefab at the randomly selected spawn point's position.
//Create the enemies at a random transform
for (int i = 0; i<m_enemyCount;i++)
{
int spawnPointIndex = Random.Range (0, spawnPoints.Count);
Transform pos = spawnPoints[spawnPointIndex];
GameObject InstanceEnemies= Instantiate ( enemy[index] , spawnPoints[spawnPointIndex].position , Quaternion.identity) as GameObject;
// Create enemies and add them to our list.
EnemiesList.Add(InstanceEnemies);
}
}
A simple way to handle this is to make a local list of spawn points in your Spawner() method, so you can keep track of which spawn points you have already used. For example:
void Spawner ()
{
//create a local list of spawn points
List<Transform> availablePoints = new List<Transform>(spawnPoints);
// Create an instance of the enemy prefab at the randomly selected spawn point's position.
//Create the enemies at a random transform
for (int i = 0; i<m_enemyCount;i++)
{
//use local availableSpawnPoints instead of your global spawnPoints to generate spawn index
int spawnPointIndex = Random.Range (0, availableSpawnPoints.Count);
Transform pos = spawnPoints[spawnPointIndex];
GameObject InstanceEnemies= Instantiate ( enemy[index] , avaialableSpawnPoints[spawnPointIndex].position , Quaternion.identity) as GameObject;
// Create enemies and add them to our list.
EnemiesList.Add(InstanceEnemies);
//remove the used spawnpoint
availableSpawnPoints.RemoveAt(spawnPointIndex);
}
}
This solution ensures that your global list of spawn points will remain intact for the next time you call the Spawner() method.
Remove the already taken spawnpoints from the list spawnPoints at the end of the for-loop like this:
spawnPoints.RemoveAt(spawnPointIndex);
You might want to create a copy of that list before the for-loop though to keep the original as is.