I am making a multiplayer MMO with unity and I am stuck on syncing up enemy spawning between clients. Basically the enemies spawn as intended, but only on the user that spawned them. Here is my code (Sorry it is a bit long, just wanted to include any information that could be relevant, as the is my first time with networking):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
[System.Serializable]
public class EnemySpawn {
public GameObject prefab;
public int difficulty;
public int rarity;
[HideInInspector()]
public GameObject instance;
}
public class EnemySpawner : NetworkBehaviour {
public EnemySpawn[] enemys;
public int maxDiff = 8;
public int spawnDistMin = 15;
public int spawnDistMax = 35;
public int spawnLvlDist = 30;
private int diffOnScreen;
private EnemySpawn enemyToSpawn;
public List<EnemySpawn> enemysOnScreen = new List<EnemySpawn>();
void Start() {
StartCoroutine (Spawn());
}
void Update() {
List<EnemySpawn> newEnemysOnScreen = new List<EnemySpawn>();
foreach(EnemySpawn c in enemysOnScreen) {
if (c.instance == null) {
diffOnScreen -= c.difficulty;
} else {
newEnemysOnScreen.Add (c);
}
}
enemysOnScreen = newEnemysOnScreen;
}
//IMPORTANT PART!!! \/\/\/
[Command]
void CmdSpawn(Vector3 pos) {
GameObject spawning = Instantiate (enemyToSpawn.prefab, pos, Quaternion.identity);
NetworkServer.Spawn (spawning);
diffOnScreen += enemyToSpawn.difficulty;
enemysOnScreen.Insert (0,enemyToSpawn);
enemysOnScreen [0].instance = spawning;
spawning.GetComponent<MonsterMain>().lvl = Mathf.RoundToInt(spawning.transform.position.magnitude / spawnLvlDist);
}
IEnumerator Spawn() {
while (true) {
int n = 0;
while (n != 1) {
enemyToSpawn = enemys [Random.Range (0, enemys.Length)];
n = Random.Range (1, enemyToSpawn.rarity);
}
while (maxDiff - diffOnScreen <= enemyToSpawn.difficulty) {
yield return null;
}
yield return null;
Transform player = Player.localPlayer.transform;
Vector2 randomOffset = Random.insideUnitCircle.normalized * Random.Range (spawnDistMin, spawnDistMax);
CmdSpawn(player.position + new Vector3(randomOffset.x,randomOffset.y,0));
}
}
}
I am using Unity 2017.1.3.3p3, and I am building this to IOS. It is a 2d game and I am using unity's build in networking system. Again everything works except that the enemies only spawn on one screen. I am connecting one phone to the editor. Any help is appreciated. thank you!
Related
I was making a platform that bounces the player after a certain period of time when the player touches it. I attached this code component to the GameObject, which is a tilemap, and it doesn't work properly. What's wrong with my code? The code and scriptable object are as follows.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExplosionPlatform : PlatformBase
{
public float explosionPower = 20f;
public float waitTime = 1.5f;
public float colTime = 0f;
public bool isDamageOnce = false;
public Vector2 area = new Vector2(2, 2);
public Vector2 positionModify = new Vector2(1, 0);
private void OnCollisionEnter2D(Collision2D collision)
{
StartCoroutine(Explosion());
}
private IEnumerator Explosion()
{
Debug.Log("ASDF");
yield return new WaitForSeconds(waitTime);
Collider2D[] player = Physics2D.OverlapBoxAll(transform.position + (Vector3)positionModify, area, 0);
foreach (Collider2D col in player)
{
if (col.CompareTag("Player"))
{
col.gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * explosionPower, ForceMode2D.Impulse);
Damage();
isDamageOnce = true;
yield return new WaitForSeconds(3f);
isDamageOnce = false;
}
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(transform.position + (Vector3)positionModify, area);
}
}
I have confirmed that the code below works properly on GameObject, which is Unity 2d Sprite. Is there any way to use that code while using the tile map?
Hello everyone I have a question. I have 2 script files on Unity.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Rendering.Universal;
public class CycleFinal : MonoBehaviour
{
//Lights
public Light2D GlobLight;
public Light2D SptLight;
//Displays
public Text timeDisplay;
public Text moveDisplay;
public Text phaseDisplay;
//Others
[SerializeField]
public float cdTimer = 20f;
public float moveCount = 20f;
void Start()
{
timeDisplay.GetComponent<Text>();
moveDisplay.GetComponent<Text>();
phaseDisplay.GetComponent<Text>();
moveCount = 0f;
DayTime();
}
void Update()
{
cdTimer -= Time.deltaTime;
if(cdTimer <0){
NightTime();
}else if(moveCount <0){
DayTime();
}
timeDisplay.text = "Day Time left: " + Mathf.Round(cdTimer);
moveDisplay.text = "Movement left: " + Mathf.Round(moveCount);
}
public void DayTime(){
phaseDisplay.text = "Day";
SptLight.intensity = 0;
GlobLight.intensity = 1;
moveCount = 0;
}
public void NightTime(){
phaseDisplay.text = "Night";
SptLight.intensity = 1;
GlobLight.intensity = 0;
moveCount = 20;
cdTimer = 0;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class PlayerController : MonoBehaviour
{
public CycleFinal script;
private PlayerMovement controls;
[SerializeField]
private Tilemap groundTilemap;
[SerializeField]
private Tilemap collisionTilemap;
private void Awake() {
controls = new PlayerMovement();
}
private void OnEnable() {
controls.Enable();
}
private void OnDisable() {
controls.Disable();
}
void Start()
{
controls.Main.Movement.performed += ctx => Move(ctx.ReadValue<Vector2>());
}
private void Move(Vector2 direction){
if(CanMove(direction))
transform.position += (Vector3)direction;
}
private bool CanMove(Vector2 direction){
Vector3Int gridPosition = groundTilemap.WorldToCell(transform.position + (Vector3)direction);
if(!groundTilemap.HasTile(gridPosition) || collisionTilemap.HasTile(gridPosition) || script.moveCount <= 0)
return false;
else
return true;
}
}
I am trying to decrease moveCount variable on every move my character made. (I'm planing to do this thing on PlayerController script Move function.) I accomplished this while moveCount variable is a variable outside of the NightTime function but when I move that moveCount variable in to the NightTime function I couldn't change its value. How can I do that?
Also main thing I want to do with those 2 script is the player will have x seconds of day time and x moves on nighttime when x seconds ends the game turns into night time and when x moves ends the game turns into day time.
I am currently building my first multiplayer game in unity and having a little bit of an issue changing/transmitting the players direction over the network.
My current player controls class is this:
using UnityEngine;
using System.Collections;
public class playerControls : MonoBehaviour {
#region
//Vars
//Movements Vars
public float runSpeed;
Rigidbody mybody;
Animator myAnimator;
bool playerDirectionE;
#endregion
// Use this for initialization
void Start () {
mybody = GetComponent<Rigidbody>();
myAnimator = GetComponent<Animator>();
playerDirectionE = true;
mybody.transform.eulerAngles = new Vector3(0, 90, 0);
}
// Update is called once per frame
void Update () {
}
void FixedUpdate()
{
float move = Input.GetAxis("Horizontal");
myAnimator.SetFloat("speed", Mathf.Abs(move)); //To intiate the charecter transition(move)
mybody.velocity = new Vector3(move * runSpeed, mybody.velocity.y, 0); //move charecter along the x axis, and keep y on gravity, not touching the z axis
if(move>0 && !playerDirectionE)
{
Flip();
}
else if(move<0 && playerDirectionE)
{
Flip();
}
}
void Flip()
{
playerDirectionE = !playerDirectionE;
Vector3 theScale = transform.localScale;
theScale.z *= -1;
transform.localScale = theScale;
}
}
I am using the following client information to send commands over the network:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using System.Collections.Generic;
public class PlayerSyncRotation : NetworkBehaviour {
[SyncVar(hook ="OnPlayerRotSynced")]
private float syncPlayerRotation;
[SerializeField]
private Transform playerTransform;
private float lerpRate = 15;
private float lastPlayerRot;
private float threshold = 1;
private List<float> syncPlayerRotList = new List<float>();
private float closeEneough = 0.3f;
[SerializeField]
private bool userHistoricalInterpolation;
[Client]
void OnPlayerRotSynced(float latestPlayerRotation)
{
syncPlayerRotation = latestPlayerRotation;
syncPlayerRotList.Add(syncPlayerRotation);
}
[Command]
void CmdProvideRotationsToServer(float playerRot)
{
syncPlayerRotation = playerRot;
}
[Client]
void transmitRotations()
{
if (isLocalPlayer)
{
if(CheckIfBeyondThreshold(playerTransform.localScale.z, lastPlayerRot)){
lastPlayerRot = playerTransform.localScale.z;
CmdProvideRotationsToServer(lastPlayerRot);
}
}
}
bool CheckIfBeyondThreshold(float rot1, float rot2)
{
if (Mathf.Abs(rot1 - rot2) > threshold)
{
return true;
}
else
{
return false;
}
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
lerpRotation();
}
void FixedUpdate()
{
transmitRotations();
}
void lerpRotation()
{
if (!isLocalPlayer)
{
HistoricalInterpolation();
}
}
void HistoricalInterpolation(){
if (syncPlayerRotList.Count > 0)
{
LerpPlayerRotation(syncPlayerRotList[0]);
if(Mathf.Abs(playerTransform.localEulerAngles.z - syncPlayerRotList[0]) < closeEneough)
{
syncPlayerRotList.RemoveAt(0);
}
Debug.Log(syncPlayerRotList.Count.ToString() + "syncPlayerRotList Count");
}
}
void LerpPlayerRotation(float rotAngle)
{
Vector3 playerNewRot = new Vector3(0, 0, rotAngle);
playerTransform.rotation = Quaternion.Lerp(playerTransform.rotation, Quaternion.Euler(playerNewRot),lerpRate*Time.deltaTime);
}
}
My rotation looks fine on the client, but over the network on the second client, the rotation is broken and looks very wrong.
I have attached a link to a Webm where you can see the short video snippet of my problem HERE.
Would anyone here have any input as to what I could be doing wrong or how I could fix this issue? any suggestions would be appreciated.
Instead Using your custom code, i will like to say you that use Unet NetworkTransform. It is Unet high level API with different option of customization.
A component to synchronize the position and rotation of networked
objects(more).
Unet HLAPI are open source so you can also get the coding of the networkTransform at bitbucket.
I am developing a simple 2D game. In game, I've created a prefab for charcaters. and I am changing sprite of prefab runtime. This all execute fine. Now I want to apply click event on a particular prefab clone and want to increase scale of prefab. I am attaching a c# script what I have did till now.
public class ShoppingManager : MonoBehaviour {
public static ShoppingManager instance;
[System.Serializable]
public class Shopping
{
public string CharacterName;
public Sprite CharacterSprite;
}
public GameObject CharacterPrefab;
public Transform CharacterSpacer;
public List<Shopping> ShoppingList;
private CharacterScript NewCharacterScript;
/*********************************************Awake()******************************************/
void Awake()
{
MakeSingleton ();
}
/******************************Create object of the script**********************************/
void MakeSingleton()
{
instance = this;
DontDestroyOnLoad (gameObject);
}
// Use this for initialization
void Start () {
LoadCharacters ();
}
void LoadCharacters()
{
foreach (var characters in ShoppingList) {
GameObject NewCharacter = Instantiate (CharacterPrefab) as GameObject;
NewCharacterScript = NewCharacter.GetComponent<CharacterScript> ();
NewCharacterScript.CharacterName = characters.CharacterName;
NewCharacterScript.Charcater.GetComponent<Image> ().sprite = characters.CharacterSprite;
NewCharacterScript.GetComponent<Button> ().onClick.AddListener (() => CharacterClicked (NewCharacterScript.CharacterName, NewCharacterScript.Charcater));
NewCharacter.transform.SetParent (CharacterSpacer, false);
}
}
void CharacterClicked(string CharacterName, GameObject Char)
{
StartCoroutine (IncreaseScale (Char));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}
This code triggers click event and also it increases scale but of last clone, not of clicked prefab clone. What I am missing, I can't understand. What should I correct in this. and Yeah! I am also attaching code of a script that I've added on prefab.
public class CharacterScript : MonoBehaviour {
public string CharacterName;
public GameObject Charcater;
}
create collider for your object attach the script below to it this way each object is responsible for handling its own functionalities like increasing its own size
public class characterFunctionalities: MonoBehaviour{
void OnMouseDown()
{
StartCoroutine (IncreaseScale (this.gameobject));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}
i am not sure how to do this ,my goal is to create a button that will control the player on the plane for the mobile touch devices.I created the button I am trying to make it to control the player but i am failing here is my player c# script
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour
{
public float speed = 500f, jumpHeight = 500f;
Transform myTrans;
Rigidbody2D myBody;
Vector2 movement;//used as temp holder variable before applying to velocity
bool isGrounded = true;
void Start ()
{
myTrans = this.transform;
myBody = this.GetComponent<Rigidbody2D>() ;
}
void Update ()
{
//Check if the player is grounded or not
isGrounded = Physics2D.Linecast(myTrans.position,
GameObject.Find(this.name+"/tag_ground").transform.position,
LayerMask.NameToLayer("Player"));
//Just for visualization, not needed for gameplay
Debug.DrawLine (myTrans.position,
GameObject.Find(this.name+"/tag_ground").transform.position,
Color.red);
//Works for keyboards and joysticks
#if !UNITY_ANDROID && !UNITY_IPHONE && !UNITY_BLACKBERRY && !UNITY_WINRT
Move (Input.GetAxisRaw("Horizontal"));
if (Input.GetButtonDown ("Jump"))
Jump ();
#endif
}
//
//Separate Move and Jump so they can be called externally by TouchButtons
//
public void Move(float horizontal_input)
{
movement = myBody.velocity;
if(isGrounded)//we can only move left and right if on the ground
movement.x = horizontal_input * speed * Time.deltaTime;
//Apply the movement to the player
myBody.velocity = movement;
}
public void Jump()//we can only jump if on the ground
{
if(isGrounded)
myBody.velocity += jumpHeight * Vector2.up * Time.deltaTime;
}
}
here is the touch logic script
using UnityEngine;
using System.Collections;
public class TouchLogicV2 : MonoBehaviour
{
public static int currTouch = 0;//so other scripts can know what touch is currently on screen
[HideInInspector]
public int touch2Watch = 64;
private new GUITexture guiTexture;
public virtual void Update()//If your child class uses Update, you must call base.Update(); to get this functionality
{
//is there a touch on screen?
if(Input.touches.Length <= 0)
{
OnNoTouches();
}
else //if there is a touch
{
//loop through all the the touches on screen
for(int i = 0; i < Input.touchCount; i++)
{
currTouch = i;
//executes this code for current touch (i) on screen
if(this.guiTexture != null && (this.guiTexture.HitTest(Input.GetTouch(i).position)))
{
//if current touch hits our guitexture, run this code
if(Input.GetTouch(i).phase == TouchPhase.Began)
{
OnTouchBegan();
touch2Watch = currTouch;
}
if(Input.GetTouch(i).phase == TouchPhase.Ended)
{
OnTouchEnded();
}
if(Input.GetTouch(i).phase == TouchPhase.Moved)
{
OnTouchMoved();
}
if(Input.GetTouch(i).phase == TouchPhase.Stationary)
{
OnTouchStayed();
}
}
//outside so it doesn't require the touch to be over the guitexture
switch(Input.GetTouch(i).phase)
{
case TouchPhase.Began:
OnTouchBeganAnywhere();
break;
case TouchPhase.Ended:
OnTouchEndedAnywhere();
break;
case TouchPhase.Moved:
OnTouchMovedAnywhere();
break;
case TouchPhase.Stationary:
OnTouchStayedAnywhere();
break;
}
}
}
}
//the default functions, define what will happen if the child doesn't override these functions
public virtual void OnNoTouches(){}
public virtual void OnTouchBegan(){print (name + " is not using OnTouchBegan");}
public virtual void OnTouchEnded(){}
public virtual void OnTouchMoved(){}
public virtual void OnTouchStayed(){}
public virtual void OnTouchBeganAnywhere(){}
public virtual void OnTouchEndedAnywhere(){}
public virtual void OnTouchMovedAnywhere(){}
public virtual void OnTouchStayedAnywhere(){}
}
I am following the touchlogic tuts but am struggling to move the player on mobile.Any with good examples on touch input is welcomed to give me the links for examples.thanks