Translate

lunes, 28 de mayo de 2018

Unity 3d - UNET Conceptos basicos

COMO CREAR UN SENCILLO JUEGO EN RED (PING PONG)


Manager


Network Manager. Maneja la configuracion de la red.
Para comenzar hay que incluir la informacion del GameObject que se va a instanciar en red. En este caso un prefba llamado Player1.
En Player Spawn Method se ha elegido Round Robin, que significa que de los puntos (Spawn Points) en los que se instanciaran los jugadores se haran de forma secuencial. Si hay dos puntos y tres jugadores primero se instanciaran en el punto 1, despues en el punto 2 y el tercer jugador se volvera a instanciar en el punto 1.
Para poder instanciar GameObjects que no sea el objeto inicial del jugador se introduciran los objetos en "Registered Spawnble Prefabs".

Network Manager HUD. Con este script se va a activar los botones superiores para la conexion en red.




Player1 y Pelota PREFAB:
La siguiente configuración es la que tiene que tener minimamente un objeto para se visualizado remotamente.



Para el prefab Player1 (izquierda):

Network Identity: Este script le asigna un numero secuencial que identifica el objeto en la red. En este caso tiene seleccionado que es un objeto que va a ser utilizado por el jugador local y que solo el tiene autoridad para manejarlo ("local player authority").

Network Transform: Este script permite enviar la localizacion del objeto a los demas jugadores. En este caso al utiizar la propiedad KINEMATIC en el objeto solo se pasaria la informacion del Transform del objeto "Transform Sync Mode".


En el caso del objeto Pelota (configuración de la derecha), el cual si que se deja que la fisica de Unity actúe sobre el objeto el script Network Transform cambia ligeramente y se le pide que envíe toda la información relativa al RIGIDBODY (que es el script que aplica toda la física en Unity).


SpawnPoints:

Los spawn points son los objetos donde se van a instanciar los jugadores.


En el caso de PING PONG solo se necesitan dos spawn points, uno a la izquierda y el otro a la derecha.

El único componente de red que necesita este objeto es Network Start Position y Network Identity. En el Transform pondremos las coordenadas y la rotación en la que queramos que aparezcan los jugadores.














Scripts:

Para poder utilizar Scripts en objetos en red es necesario cambiar MONOBEHAVIOR por NETWORBEHAVIOUR. También es necesario añadir UnityEngine.Networking (ambos en rojo).

En este script debido a que todo el movimiento se esta controlando con el componente Network Transform no se envía ninguna información a la red directamente. El script se comporta igual que si estuviera en un juego normal (como si se utilizara MonoBehaviour).

Player.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;


public class Player : NetworkBehaviour {
    [SerializeField][Range(1,20)]
    public float speed = 10;
    public Vector3 targetPosition;
    private Vector3 targetPositionTemp;
    private bool isMoving;

public int direccion;
public float posiAnterior;
public float posInicial = -8.0f;

    const int LEFT_MOUSE_BUTTON = 0;
const int ARRIBA = 2;
const int ABAJO = 0;
const int IGUAL = 1;

    private Rigidbody rb;

public override void OnStartLocalPlayer()
{
GetComponent<MeshRenderer>().material.color = Color.blue;
}

    void Start()
    {
posInicial = this.transform.position.x;
        targetPosition = transform.position;
        isMoving = false;
posiAnterior = targetPosition.z;
    }

    void Update()
    {
if (!isLocalPlayer)
return;
        if (Input.GetMouseButton(LEFT_MOUSE_BUTTON))
            SetTargetPosition();
        if (isMoving) MovePlayer();
    }

void OnTriggerEnter(Collider other){

}

    void SetTargetPosition()
    {
Plane plane = new Plane(Vector3.up,new Vector3(posInicial,transform.position.y,transform.position.z));
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        float point = 0f;

        if (plane.Raycast(ray, out point))
            targetPositionTemp = ray.GetPoint(point);

targetPosition = new Vector3 (posInicial, targetPositionTemp.y, targetPositionTemp.z);
        isMoving = true;

    }
    void MovePlayer() {
transform.position = Vector3.MoveTowards(new Vector3(posInicial,transform.position.y,transform.position.z), targetPosition, speed * Time.deltaTime);
        if (transform.position == targetPosition) isMoving = false;
if (transform.position.z > posiAnterior) {
direccion = ARRIBA;
} else {
if (transform.position.z < posiAnterior) {
direccion = ABAJO;
} else {
direccion = IGUAL;
}
}
posiAnterior = targetPosition.z;

    }


Sin embargo al utilizar un objeto distinto al Player Prefab instanciado por cada jugador como ese el caso de la pelota si que tenemos que enviar la informacion al Servidor, en primer lugar, y despues desde el servidor a todos los clientes.
Para poder hacer esto se utilizan los comandos [Command], que envia la informacion al servidor, y [ClientRpc] que envia la informacion desde el servidor a los distintos clientes conectados. De esta forma se replica el comportamiento de las pelota en todos los clientes.

Pelota.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Pelota : NetworkBehaviour {

public float velocidadInicial = 600f;
public float velocidadBumper = 200f;

private Rigidbody rb;
public bool direcDerecha;
public int valor;
public float fuerza;
public float velocidadPelota;

public GameObject pelotaPrefab;

const int ARRIBA = 2;
const int ABAJO = 0;
const int IGUAL = 1;



public int puntuacionLocal;
public int puntuacionForaneo;



void Awake(){
if (isServer)
this.gameObject.SetActive (false);


    valor = Random.Range (1, 2);
if (valor == 1) {
direcDerecha = true;
} else {
direcDerecha = false;
}
rb = GetComponent<Rigidbody> ();
fuerza = 0;

}
// Use this for initialization
void Start () {
Direccion (fuerza);
}
// Update is called once per frame
void Update () {
}

void PorSiHaceFalta(){

//Mathf.Approximately(velocidadPelota,0)
}

void LateUpdate(){
if (Mathf.Approximately(rb.velocity.z,0.2f))
DireccionContinua (fuerza);
//rb.velocity = velocidadInicial * (rb.velocity.normalized);

}
void OnCollisionEnter(Collision other){
if (other.gameObject.tag == "Player") {
//Debug.Log (other.gameObject.name + " : Mas fuerza"); 
if (other.gameObject.GetComponent<Player> ().direccion == ARRIBA) {
fuerza = -100f;


} else {
if (other.gameObject.GetComponent<Player> ().direccion == ABAJO) {
fuerza = 100f;

} else {
fuerza = 0f;

}
}
Direccion (fuerza);

}

if (other.gameObject.tag == "Bumper") {
//Debug.Log ("Choca con un bumper");
rb.AddForce (rb.velocity.normalized * velocidadBumper);
}

}

public void OnTriggerEnter (Collider other){
if (other.gameObject.tag == "DetectorGol") {
if (other.gameObject.name == "BumperTrasero") {
GameObject.FindGameObjectWithTag ("ManagerJuego").GetComponent<Manager> ().AddGolLocal();
//puntuacionLocal++;
//GameObject.FindGameObjectWithTag ("Manager").GetComponent<Manager> ().puntuacionLocal = puntuacionLocal;
} else {
GameObject.FindGameObjectWithTag ("ManagerJuego").GetComponent<Manager> ().AddGolForaneo ();
//puntuacionForaneo++;
//GameObject.FindGameObjectWithTag ("Manager").GetComponent<Manager> ().puntuacionForaneo = puntuacionForaneo;
}

Invoke ("DestruirPelota", 2.0f);
}
}
public void MasFuerza(float valorZ){
rb.AddForce(new Vector3(velocidadInicial,velocidadInicial,valorZ));
}
public void MasFuerza2(float valorZ){
rb.AddForce(new Vector3(-1*velocidadInicial,velocidadInicial,valorZ));
}

public void DestruirPelota(){

Cmd_Pelota ();
Destroy (this.gameObject);
}

[Command]
public void Cmd_Pelota(){
GameObject nuevaPelota = Instantiate (pelotaPrefab);
nuevaPelota.transform.position = new Vector3 (0f, 0.25f, 0f);
nuevaPelota.name = this.name;
NetworkServer.Spawn (nuevaPelota);
Rpc_Pelota (nuevaPelota.transform.position);
}
[ClientRpc]
public void Rpc_Pelota(Vector3 posicion){
this.transform.position = posicion;
}
public void Direccion(float valorZ){
if (!direcDerecha) {
MasFuerza (valorZ); 

} else {
MasFuerza2 (valorZ);
}
direcDerecha = !direcDerecha;
}

public void DireccionContinua(float valorZ){
if (!direcDerecha) {
MasFuerza (valorZ); 

} else {
MasFuerza2 (valorZ);
}
}

}

Por ultimo tenemos el script manager que incrustado en el objeto ManaJuego. En este script se controla, desde el numero de jugadores conectados, a la puntuacion de los marcadores mostrada en los clientes.

Manager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Manager : NetworkBehaviour {

public GameObject pelota;
public Transform pelotaPosInicio;
public bool comienzoJuego = false;

public GameObject panelBoton;
public GameObject titulo;

public int puntuacionLocal;
public int puntuacionForaneo;
public Text marcadorLocal;
public Text marcadorForaneo;
// Use this for initialization
void Start () {
if (!isServer)
return;

}
// Update is called once per frame
void Update () {
if (!isServer)
return;
if (NetworkManager.singleton.numPlayers == 2 && !comienzoJuego) {
comienzoJuego = true;
puntuacionLocal = 0;
puntuacionForaneo = 0;
Cmd_Inicio ();
Cmd_SpawnPelota ();

}
//Debug.Log (NetworkManager.singleton.numPlayers);
}

[Command]
void Cmd_SpawnPelota(){
GameObject go = Instantiate (pelota, pelotaPosInicio);
NetworkServer.Spawn (go);
}

public void AddGolLocal(){
puntuacionLocal++;
Cmd_Puntuacion ();
}

public void AddGolForaneo(){
puntuacionForaneo++;
Cmd_Puntuacion ();
}

[Command]
void Cmd_Puntuacion(){

marcadorLocal.text = puntuacionLocal.ToString ();
marcadorForaneo.text = puntuacionForaneo.ToString ();
Rpc_Puntuacion (puntuacionLocal, puntuacionForaneo);
}

[ClientRpc]
void Rpc_Puntuacion(int pLocal, int pForaneo){
marcadorLocal.text = pLocal.ToString ();
marcadorForaneo.text = pForaneo.ToString ();

}

[Command]
void Cmd_Inicio(){

panelBoton.SetActive (false);
titulo.SetActive (false);
Rpc_Inicio();
}

[ClientRpc]
void Rpc_Inicio(){
panelBoton.SetActive (false);
titulo.SetActive (false);

}

public void Salir(){
Application.Quit ();
}

}

No hay comentarios:

Publicar un comentario