Páginas

sábado, 11 de outubro de 2014

Aula 10 MetaVerso

Já vimos como ir carregando até 10 "almas" no nosso jogo (só 5 simultâneas), mas o corpo delas era sempre o mesmo "demiurgo prateado".
Na aula anterior vimos como serializar avatares (seus submeshes) e podemos colocá-los, com as texturas, disponíveis na Rede (internet).
O que vamos fazer para completar nosso sistema (o tal Metaverso, onde os avatares de jogos são independentes) é ver como o jogador pode definir qual "corpo teleportável" vai assuimir a "alma" designada para ele pelo seu número de "entrante".
Vamos ver como ficam os dois scripts: o do Compacto Duplo (que chamamos de : ServCli e o script controlador do movimento do avatar e instancialização. Dê uma olhada geral, que depois vamos analisar os detalhes. Primeiro o ServCli:
var IPServidor = "127.0.0.1";
var IPServ = "";
var endSubmeshTroncoI="www.dmu.com/troncoAVATAR1.data";
var endTexturaTroncoI="www.dmu.com/troncoAVATAR1.jpg";
var endSubmeshBracosI="www.dmu.com/bracosAVATAR1.data";
var endTexturaBracosI="www.dmu.com/bracosAVATAR1.jpg";
var endTexturaPernasI="www.dmu.com/pernasAVATAR1.jpg";
var escalaTroncoI = "0.30";
var escalaBracosI = "0.30";
var endSubmeshTronco =" ";
var endTexturaTronco =" ";
var endSubmeshBracos =" ";
var endTexturaBracos =" ";
var endTexturaPernas =" ";
var escalaTronco= 0.30;
var escalaBracos = 0.30;
function OnGUI (){
  
 if (Network.peerType == NetworkPeerType.Disconnected){
  IPServidor = GUI.TextField(new Rect(120,10,100,20),IPServidor);
  GUI.Label(new Rect(10,90,400,20),"Corpo: endereco dos meshes, textura e escala");
  endSubmeshTroncoI = GUI.TextField(new Rect(10,110,300,20),endSubmeshTroncoI );
  endTexturaTroncoI = GUI.TextField(new Rect(10,130,300,20),endTexturaTroncoI);
  escalaTroncoI = GUI.TextField(new Rect(10,150,50,20),escalaTroncoI);
  GUI.Label(new Rect(10,170,400,20),"Bracos: endereco dos meshes,textura e escala");
  endSubmeshBracosI = GUI.TextField(new Rect(10,190,300,20),endSubmeshBracosI);
  endTexturaBracosI = GUI.TextField(new Rect(10,210,300,20),endTexturaBracosI);
  escalaBracosI =  GUI.TextField(new Rect(10,230,50,20),escalaBracosI);
  GUI.Label(new Rect(10,250,250,20),"Pernas: endereco da textura");
  endTexturaPernasI = GUI.TextField(new Rect(10,270,300,20),endTexturaPernasI);
  if (GUI.Button (new Rect(10,10,100,30),"Conectar")){
   Carregar();
   Network.Connect(IPServidor, 25000);
  }

  if (GUI.Button (new Rect(10,50,100,30),"Start Servidor")){
   Carregar();
   Network.InitializeServer(5, 25000,false);
   // Proclamação no Servidor de que foi iniciado o servidor  
   for (var go : GameObject in FindObjectsOfType(GameObject)){
    go.SendMessage("noAr", SendMessageOptions.DontRequireReceiver); 
   }
  }
 }
 
 else{ //Se startou  ou conectou
  if(Network.isServer){
   IPServ = Network.player.ipAddress;
   GUI.Label(new Rect(140,20,250,40),"Para jogador usar: "+IPServ );
  }
    
  if (GUI.Button (new Rect(10,10,100,30),"Sair")){
   if(Network.isServer){
    networkView.RPC("DesligarCL", RPCMode.Others,"Vai desligar");
   }
   
   Network.Disconnect();
   Application.Quit();
  }
 }
}
 
function Carregar(){
 endSubmeshTronco =endSubmeshTroncoI;
 endTexturaTronco =endTexturaTroncoI;
 endSubmeshBracos =endSubmeshBracosI;
 endTexturaBracos =endTexturaBracosI;
 endTexturaPernas =endTexturaPernasI;
 escalaTronco = parseFloat(escalaTroncoI);
 escalaBracos= parseFloat(escalaBracosI);
}
function OnConnectedToServer() {
 // Proclamação gerada no  Clente quando feita conexão 
 for (var go : GameObject in FindObjectsOfType(GameObject))
  go.SendMessage("noAr", SendMessageOptions.DontRequireReceiver);  
}
 

@RPC
function DesligarCL(recebido : String){
  Application.Quit();
}
  
  
@RPC
function CarregaCL(ent: String,mT: String,tT: String,mB: String,tB: String,tP: String,eT: float,eB: float){

 //mesh headbody
 var GOH = GameObject.Find("headbody" + ent);
 var urlH= mT;
 var downloadH = WWW(urlH);
 yield downloadH;
 var meshH = MeshSerializer.ReadMeshFromWWW(downloadH );
 if (!meshH) return;
 var meshFilterH : MeshFilter = GOH.GetComponent(MeshFilter);
 meshFilterH.mesh = meshH;
 GOH.transform.localScale =Vector3.Scale(GOH.transform.localScale,Vector3(eT,eT,eT));
 //textura headbody
 var urlTH = tT; 
 var downloadTH = WWW(urlTH);
 yield downloadTH; 
 GOH.renderer.materials[0].mainTexture = downloadTH.texture;
  
 //mesh arms
 var GOA =GameObject.Find("arms" +ent);
 var urlA = mB;
 var downloadA = WWW(urlA);
 yield downloadA;
 var meshA = MeshSerializer.ReadMeshFromWWW( downloadA );
 if (!meshA) return;
 var meshFilterA : MeshFilter = GOA.GetComponent(MeshFilter);
 meshFilterA.mesh = meshA;
 GOA.transform.localScale =Vector3.Scale(GOA.transform.localScale,Vector3(eB,eB,eB));
 //textura arms
 var urlTA = tB; 
 var downloadTA = WWW(urlTA);
 yield downloadTA; 
 GOA.renderer.materials[0].mainTexture = downloadTA.texture;
  
 //textura legs
 var GOL =GameObject.Find("legs" +ent);
 var urlTL = tP; 
 var downloadTL = WWW(urlTL);
 yield downloadTL; 
 GOL.renderer.materials[0].mainTexture = downloadTL.texture;

}
Vamos ver então o script controlador de avatares, que também cuida da instancialização.
var velocidade = 6.0;
var velSalto = 8.0;
var gravidade = 20.0;
private var deltaDirecao : Vector3;
 
var Player : GameObject;
var contr  : CharacterController;
var playerClone = "";
var avatar0: Transform;
var avatar1: Transform;
var avatar2: Transform;
var avatar3: Transform;
var avatar4: Transform;
var avatar5: Transform;
var avatar6: Transform;
var avatar7: Transform;
var avatar8: Transform;
var avatar9: Transform;
var avatar10: Transform;
var entrante = "";

var servcli: ServCli;
  
 function noAr(){
 
  if(Network.isServer){
   Network.Instantiate(avatar0, transform.position, transform.rotation, 0);
   playerClone = "Player0(Clone)";  
 }
 else{ 
   entrante = Network.player.ToString();
   if(entrante =="1")Network.Instantiate(avatar1, transform.position, transform.rotation, 0);
   if(entrante =="2") Network.Instantiate(avatar2, transform.position, transform.rotation, 0);
   if(entrante =="3")Network.Instantiate(avatar3, transform.position, transform.rotation, 0);
   if(entrante =="4")Network.Instantiate(avatar4, transform.position, transform.rotation, 0);
   if(entrante =="5")Network.Instantiate(avatar5, transform.position, transform.rotation, 0);
   if(entrante =="6")Network.Instantiate(avatar6, transform.position, transform.rotation, 0);
   if(entrante =="7")Network.Instantiate(avatar7, transform.position, transform.rotation, 0);
   if(entrante =="8")Network.Instantiate(avatar8, transform.position, transform.rotation, 0);
   if(entrante =="9")Network.Instantiate(avatar9, transform.position, transform.rotation, 0);
   if(entrante =="10")Network.Instantiate(avatar10, transform.position, transform.rotation, 0);
    
  playerClone = "Player"+entrante+"(Clone)"; 
  }
  Player = GameObject.Find(playerClone);
  contr =  Player.GetComponent(CharacterController);
  if(Network.isClient)networkView.RPC("CarregaCL", RPCMode.AllBuffered,entrante, servcli.endSubmeshTronco,servcli.endTexturaTronco,servcli.endSubmeshBracos,servcli.endTexturaBracos,servcli.endTexturaPernas,servcli.escalaTronco,servcli.escalaBracos);
}

function Update(){
  
   if (contr.isGrounded){
    Player.transform.eulerAngles.y += Input.GetAxis("Horizontal"); //para girar
    deltaDirecao = Vector3(0, 0,Input.GetAxis("Vertical"));
    deltaDirecao = Player.transform.TransformDirection(deltaDirecao);
    deltaDirecao *= velocidade;

     if (Input.GetButton ("Jump")) {
      deltaDirecao.y = velSalto;
     }
   }

  deltaDirecao.y -= gravidade * Time.deltaTime;
  contr.Move(deltaDirecao * Time.deltaTime); 

  
}

function OnPlayerDisconnected (player : NetworkPlayer){
  
 Network.RemoveRPCs(player, 0);
 Network.DestroyPlayerObjects(player);
}
Esse segundo script só tem de diferente as linhas em azul, em que, primeiro criamos um prefixo (objeto referência) para acessar variáveis do outro script.
var servcli: ServCli;
(Lembrar de fazer a conexão no Editor, como já vimos em outros casos.)
E usamos essas variáveis, que contém os endereços dos submeshes teleportáveis e texturas, numa RPC que vai ser executada em cada cópia de cliente, carregando o avatar do entrante, já customizado.
Melhor então vermos como tudo começa no script Servidor/Cliente:
Nas primeiras linhas, montamos a interface inicial, com os campos onde o jogador vai entrar com os endereços antes de apertar "Conectar". Ja vem com uns endereços:



Importante notar que, além dos endereços dos dois submeshes ("headbody" e "arms"), temos que dizer qual sua escala de ampliação ou redução de tamanho. Normalmente o valor 0.30 é adequado mas, conforme determinadas situações (se o criador de modelos não é o Blender, por exemplo), o modelo pode ser serializado num tamanho muito aumentado ou diminuido em relação ao adequado. E o modelador terá que descobrir e informar qual o valor de "escala" para corrigir isso. Se isso acontecer com você, lembre-se desse aviso.
Entrados os endereços e feita a conexão (ou start do Servidor) vamos para a instancialização das "almas" (no outro script) e, só depois de termos as "almas", podemos convocar os "corpos teleportáveis", o que é feito pela RPC: CarregaCL.
Nessa RPC é usado novamente o "número de entrante" para pegar os submeshes equivalentes do "demiurgo prateado" que está com a "alma" e substituí-los pelos submeshes do "corpo teleportável", que chega num download e é "de-serializado". É aplicada nessa hora o índice de "escalagem", para aumentar ou diminuir o tamanho dos submeshes. As texturas também sofrem downloads e são aplicadas.
NOTA: Colocar uma cópia do script MeshSerializer no "Administracao">
No fim das contas, o sistema é relativamente simples. Mas o resultado é muito bom. Você pode fazer testes com nossa demo, usando avatares e texturas que encontra na sessão de nosso site chamada: "Galeria de Avatares e Roupas" - ver link na parte superior da página. Mas, o ideal é que você crie seu avatar customizado e faça testes com ele. Veja só: você vai criar uma avatar a seu gosto e entrar na nossa aplicação, nosso espaço. Isso é o chamado: "Metaverso".
Baixe a demo zipada :

Nenhum comentário:

Postar um comentário

Pesquisar