[SourceForge] JMMORPG Project

89 respostas
M

Projeto (Open-Source) para a criação de MMORPGs em Java.
JMMORPG Project: https://sourceforge.net/projects/jmmorpg/

  • Apresentação (Autor) -

Michel Pinheiro Montenegro

Mais de 12 anos de experiência profissional na área da Tecnologia da Informação. Bacharel em Sistemas de Informação pelo Instituto de Estudos Superiores da Amazônia - IESAM. Esta cursando sua pós-graduação em Latu Sensu em Engenharia de Sistemas pela Escola Superior Aberta do Brasil - ESAB. Participação em grandes projetos para o Estado como Transparência-Pará (Sistema Web desenvolvido com o objetivo de manter expostas as informações relacionadas aos gastos públicos de todo o estado do Pará) e o Siscon (Sistema de Controle de Auditorias, um dos sistemas pioneiros no que se refere a Órgãos de Controle Interno no País (Auditorias e Controladorias). Possui 16 cursos extracurriculares na área de T.I.

. E-mail: [email removido]

  • Objetivo-

Criar um Tutorial Map, que deve ter todas as funções disponiveis para o jogo (Sistema de Lutas, Eventos, etc…), para ser o Core (Nucléo do Projeto), e no lado servidor idem, o JMMORPG deve ser a base para a criação de outros tipos de mmorpgs (Ex: Sistema de luta em modo tatics [Dofus], colisão [Tibia], etc…). Claro que vamos projetar e evoluir um jogo real para ser usado e servir como exemplo.

-> O que já existe?

  • Ele já tem algumas funcionalidades:
  1. Sistema de Criação do mapa e objetos sem criar conflitos.
    Exemplo de uma funcionalidade do mapa: Efeito do Heroi passar por trás de uma arvore.

  2. Sistema de Movimento.

  3. Sistema de Colisão: Baseado no mapa criado pelo TiledEditor (Respeitando o padrão seguido pelo projeto), colisão em objetos não indicados no mapa TiledEditor (Ex: NPCs). falta checar com objetos moveis (Monstros).

  4. Sistema de checagem e delimitação do mapa (Ver item 5).

  5. Sistema de Camera:
    a) A camera anda junto com o heroi
    b) A camera para de acompanha-lo quando ela chega no limite do mapa
    Obs: Fiz um mini-map e esta funcionando, mas vou oculta-lo já que ele deve ser desenhado depois em um GUI.

  6. Sitema de Eventos:
    Ex: O heroi chega na área do npc e aperta “barra de espaço” e dispara o evento equivalente aquele npc.

  • Organização baseada em -
Estates {

=> System 

=> Entity 

-----> component 

-----> Spatial 

}

Não preciso dizer que aceitamos colaboradores no desenvoilvimento ou “Tirando duvidas, indicar links, opinar sobre algo no projeto, etc…”

89 Respostas

V

Boa sorte.
Vou companhar esse projeto de perto.

M

Screens:



V

Legal, você usou a Slick 2D.

Mas a taxa de FPS não está meio baixa, não? Para um jogo simples como esse, numa máquina modesta, ela deveria ser 60FPS, se você estiver usando v-sync, ou mais de 300FPS, sem.
Principalmente porque suas texturas parecem bastante simples.

E no servidor, o que você está usando? É o RedDwarf?

V

Outra coisa, a movimentação vai ser sempre quadrada, como no zelda, ou você está pensando em deixa-la vetorial? Sua colisão é feita por pixel, ou através de bouding box?

M

Ele ta a 30FPS porque eu coloquei como limite 35.

<blockquote>private static final int TARGET_FRAME_RATE = 35;

…

container.setTargetFrameRate(TARGET_FRAME_RATE);

</blockquote>

Obs 1: VSYNC = true;

Obs 2: Lembro que a visão humana não percebe mais do que 30fps.

Teria alguma vantagem em manter um FPS, mais alto que isso?

Usei o Slick2d(Que us LWJGL) + Artemis.

O sistema de colisão atual foi feto no braço (Usei o phys2d no começo, tive um problema com delay, descobri que nem era culpa do framework, mas como já tinha mudado, resolvi manter o sistema atual, deixando margem para mudar a qualquer momento para um sistema mais sofisticado). Por hora vou deixa-la quadrada (Mas penso em deixar vetorial sim o movimento).

Estou usando intersect entre poligonos (Estou estudando colisão por “bouding box”), junto com a imagem(objeto) crio um Poligono que o acompanha (checando as interseções com outros objetos (polignos)).

Não estou usando o “RedDwarf”, por hora vai ser na mão (RMI para algumas ações e UDP para outras). Mas pretendo no minimo ver como o reddwarf vai se comportar com o projeto (Nunca o usei ou testei).

V

Quanto aos 24 FPS, dê uma lida:
http://www.100fps.com/how_many_frames_can_humans_see.htm

De qualquer forma, quando for tirar Screenshots para sua engine, sugiro tirar fora toda e qualquer limitação, até para termos uma boa noção do quão eficiente a parte de desenho está. Ou para ninguém ter a impressão errada que tive.
Ou então, simplesmente retire a informação do FPS da cena.

Acho que com RMI será muito difícil que esse jogo seja um MMO. Ele será, no máximo, um jogo multiplayer (o primeiro “m” refere-se a “massive”, o que exige estratégias específicas para lidar com milhares de conexões simultâneas).
Também seria bom você testar esse jogo em internet, pois o grande problema de jogos massivos é, além do congestionamento de banda e da performance do servidor, os atrasos causados pela própria rede. Contormamos isso através de Dead Reckoning, ou seja, gerando uma previsão de onde o cliente estará e deixando que o cliente comece suas ações (movimentação, ataques, etc) antes de obter uma confirmação do servidor. Caso contrário, o jogo fica “lagado” muito rapidamente, causando grande desconforto aos jogadores.

A

Olá Michel.Montenegro,

Cara eu me candidato a colaborador, tenho o período da noite livre. Achei interessantíssimo, e sempre tive vontade de participar de algo do tipo. Qualquer coisa, me manda uma MP.

Abraço.
Adriel Oliveira.

M

ViniGodoy
Bem falado, vou lebrar disso nasproximas screens.

Vini de uma olhada neste projeto “http://www.l2jserver.com/” (Open-Source e Free) este servidor eu já vi com até 2K Players (Dizemque tem com mais), e eles usam RMI e comunicação direta via UDP (Ainda não sei, quais os casos). Ainda não passei por estes problemas em relação a comunicação to deixando 1° o lado cliente “pronto para a evolução”. Mas até lá vamos resolver isso.

Adriel
te mandei uma PM.

Quem quiser colaborar e interagir com o projeto, me adiciona.
Meu Gtalk: [email removido]
Meu Msn: [email removido]
Meu Skype: michel.montenegro (Belém - PA)

A

Achei que apenas eu desse forum conhecia a serie Zelda.

M

* Adicionado Transição entre Mapas:

  • Agora da pra criar qualquer mapa (respeitando umas regras), que o heroi pode transitar facilmente entre telas/cenarios (Ex: Mapa->Dungeon->dungeon Nivel 1…->Mapa, etc…).

  • No que se refere a codigo com 3 linhas se tem a mudança do mapa sem stress. ^^
    Obs: Leiam a “documentacao.txt” do projeto pra conhecer os padrões.

* Adicionado Teleporte:

  • Em mapas grandes, podem ter pontos de teleport, e a camera acompanha o teleport do personagem (Ficando no local certo e respeitando limite do mapa).

============================
Agora vou adicionar o sistema de coleta de itens (ex: Pegar item do chão (colher flores, recolher itens caidos, …)), mas estou na duvida em como organizar isso, pensei em organizar desta forma:
=> Entity Hero
. Component Inventario
----(Atributo) inventario(Tipo Coleção deObjects): aqui ele guardaria os objetos do chão.
----(Atributo) arma(Tipo Weapon): Adiciona dano ao ataque fisico do heroi (Por hora não vai fazer nada além de exstir rsrsrs…).
----(Atributo) escudo(Tipo Shield) … (Tipo Bota) etc…

Porém to pensando em as classes Weapons, Shields… como Componentes separados do inventario. Alguem tem alguma sugestão?

J

Ei…muito legal esse projeto!

M

Obrigado, ele ta saindo aos poucos, mas o que tem ta sendo feito com cuidado. Quero qualidade e capacidade de adaptação.
To vendo se tem algo que possa trabalhar em cima, que não dependa de acesso a algum BD ou ao lado servidor, por horaquero adiantar o maximo de coisas no lado cliente.

M

Alguem aqui que já usou o reddwarf poderia me da uma mão?
Comecei a estuda-lo, em tese pretendo usa-lo no lado servidor (Vique tem jogos que o utilizam no lado servidor, vi no site dele)

M

Adicionado imagens ao projeto.
Link: https://sourceforge.net/projects/jmmorpg/

M

ViniGodoy
O projeto Slick-SUI e FengGUI foram abandonados?

M
  • Depois de muito sofrer, estudar, pesquisar, estou trabalhando com o Nifty-GUI ( Inclusive é usado no Jmonkeyengine para jogos 3D ).

  • Uma screen do UI temporario do projeto, vou modelar o scomponentes finais ainda. Vocês não fazem ideia do parto que foi isso, o NiftyGUI na versão 1.3 não tem documentação praticamente e tudo aponta pro 1.2 (MUITO diferente o 1.3).

Uploaded with ImageShack.us

M

Erro ao usar o metodo Render por Seção.
===>>>>> Preciso de ajuda aqui!

Estou desenhando somente a seção do mapa visivel na tela, oque ta fora não é desenhado. Ele ta funcionando direito, porém estou com um problema. De vez emquando o mapa da pulosno desenho.

  • Isso ocorre porque o heroi anda de 4 em 4 pixes, e sua cordenada XY tem que ser convertida em Tiles (Ex: heroPosX(128)/32= Tile 4).
  • Só que quando o heroi anda, o fundo (Chão) fica dando saltos. Gostaria de corrigir isto, mas nã osei como.

Eu realmente gostaria de uma ajuda nisso, com certeza não sou o unico que já passou por isso.

  • Informações que vão ajudar:

* Metodo em que ocorre o problema:

public void renderMapSprites() {
		cameraSystem  = world.getSystemManager().getSystem(CameraSystem.class);
		int offPixelX = (int) cameraSystem.getOffsetX()*-1; // (? * -1) for positive value
		int offPixelY = (int) cameraSystem.getOffsetY()*-1; // (? * -1) for positive value
		int offTileX  = offPixelX/32;
		int offTileY  = offPixelY/32;
		int visx = 25;
		int visy = 21;
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 1 , true); //layer oceano
	}

* Documentação

<blockquote>	/**

* Render a section of the tile map

*

* @param x The x location to render at

* @param y The y location to render at

* @param sx The x tile location to start rendering

* @param sy The y tile location to start rendering

* @param width The width of the section to render (in tiles)

* @param height The height of the secton to render (in tiles)

* @param l The index of the layer to render

* @param lineByLine True if we should render line by line, i.e. giving us a chance

* to render something else between lines (@see {<a class="mention" href="/u/link">@link</a> #renderedLine(int, int, int)}

*/

public void render(int x,int y,int sx,int sy,int width,int height,int l,boolean lineByLine)</blockquote>
M

Aqui esta a correção do problema:
By PEJUGE

public void renderMapSprites() {
		cameraSystem  = world.getSystemManager().getSystem(CameraSystem.class);
		Entity hero =  world.getTagManager().getEntity("Hero");
		Transform transformHero = transformMapper.get(hero);

		int offPixelX = (int) -cameraSystem.getOffsetX(); 
		int offPixelY = (int) -cameraSystem.getOffsetY();
		offPixelX = offPixelX - offPixelX % 32;
		offPixelY = offPixelY - offPixelY % 32;
		
		int offTileX  = (offPixelX)/32;
		int offTileY  = (offPixelY)/32;
		int visx = 27;
		int visy = 21;
		//System.out.println(cameraSystem.getOffsetX()*-1 + "=" +cameraSystem.getOffsetY()*-1);
		System.out.println(offPixelX + "=" + offPixelY);
		System.out.println(offTileX + "-" +offTileY);
		//Desenhando o mapa
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 1 , true); //layer oceano
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 2, true); //layer ilha
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 3, true); //layer obstaculos
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 4, true); //layer enfeites
		
		ImmutableBag<Entity> npcs = world.getGroupManager().getEntities("Group: Npc");
		for(int b = 0; npcs.size() > b; b++) {
			Entity npc = npcs.get(b);
			Transform transformNPC = transformMapper.get(npc);
			Spatial spatial = this.managerMap.getSpatials().get(npc.getId());
			if (transformNPC.getX() >= 0 && transformNPC.getY() >= 0 && spatial != null) {
				   spatial.render(graphics);
			}
		}
		
		Spatial spatial = this.managerMap.getSpatials().get(hero.getId());
		if (transformHero.getX() >= 0 && transformHero.getY() >= 0 && spatial != null) {
			   spatial.render(graphics);
		}
		
		this.managerMap.getTmap().render(offPixelX, offPixelY, offTileX, offTileY, visx, visy, 5 , true); //layer céu
	}
M

Algumas duvidas com o lado servidor de um jogo multiplayer.

Li bastante sobre qual o melhor protocolo para um MMOG, falam muito que UDP é mais recomendado, pois o TCP pode gerar travamentos no jogo se não tiver a resposta no “Hand sShake”.

Vi que existem “protocolos” mais especificos para jogos, não entendi bem isso, se alguem souber explicar melhor agradeço.

Mas estamos com outras duvidas também, são elas:

  1. É interessante que fique uma classe(thread) rodando e capturando e enviando as informações para o/do servidor? ou dentro das outras threas ficar fazendo isso em cada metodo especifico?

  2. Sobre o tratamento de conexões, como vocês fazem?
    a) Abre conexão -> executam tarefa -> fecha a conexão (Isso pra cada tarefa exigida, mesmo as feitas por segundo como andar no mapa).
    b) Abre conexão -> fica executando todas as tarefas -> Quando o jogo for finalizado fecha a conexão.
    c) ???

Em nosso projeto, fizemos algo basico, onde ao andar o servidor apenas checa colisão com os objetos fixos do mapa (Ex: Muros etc…), isso já esta funcionando, mas esta bem basico o codigo, usamos UPD como protocolo. Quando fomos para algo além, como pegar os dados dos outros players, ai tivemos uns problemas, inclusive tem coisas chatas como sempre vir um monte de “[][][][][]” depois do texto que enviamos e recebemos (Usamos o metodo Split da classe String, seguido de um belo FOR para separar as informações).
Isso jáesta no ar no projeto: https://sourceforge.net/projects/jmmorpg/ (Cas oalguem queira ver e testar).

M
  • Adicionado:
    O sistema multiplayer esta funcionando, tanto no chat, quanto na exibição dos outros players logados. (O chat vai sofrer melhorias)

Nova Screen do pojeto:

R

Olá Michel,

Muito interessante o seu projeto.
Eu te adicionei no gtalk pra trocar umas idéias.

Abraço.

M

Adicionado :slight_smile:

Da uma lida neste link: [Tutorial] Instalando o projeto JMMORPG

Se tudo correr bem em breve faço outros tutoriais sobre o projeto e faço a documentação dele, mas por hora este é o principal para começar.

E

Muito fera cara, vou acompanhar também, parabens!!!

Você está fazendo a parte servidor e client junto?
Pergunto no sentido de, da para trocar o Slick2d por outra engine, e a parte server continuar funcionando?

[]s

M

Obrigado! :slight_smile:

Sim! estou fazendo junto.
Sim! O servidor é independe do cliente (Agora o cliente depende do servidor, ele não roda mais offline).

Recentemente estamos mudando o sistema de movimento para matriz, e movimentação ao estilo Dofus e Tibia (Personagem na ANIMAÇÃO anda normalmente de x em X pixels, mas pro servidor é enviado de 32 em 32 (Tamanho dos tiles)).

Na pratica da um tempo X entre o envio das atualizações do movimento ao server e não altera a animação (Anda normalmente), a colisão acaba sendo feita pela posição da matriz e não mais por interseção entre Poligonos.
Obs: Minha duvida é “Só inicio a movimentação no cliente após o servidor confirmar ou inicio a movmentação e se oservidor disser “Não” ele corrige o cliente (Isso pode fazer o char voltar derrepente para uma posição anterior, mas …)”.

Onde eu gostaria de uma ajuda forte é com o niftygui.

Lembrete: Adicionei um ssistema de avatar, agora cada personagemtem um avatar vinculado, vou postar uma screen neste post, wait…

Uploaded with ImageShack.us

E

Michel.Montenegro:
Obrigado! :slight_smile:

Sim! estou fazendo junto.
Sim! O servidor é independe do cliente (Agora o cliente depende do servidor, ele não roda mais offline).

O servidor sendo indenpendente do cliente deixa mais legal ainda! hehehehehe

Eu tenho desenvolvido uma engine em Java2d, com o servidor indenpendendo podemos fazer uma integração depois. =)

Continue com o bom trabalho
[]s

V

Ah, que lindo, agora que vi que simplesmente parei de receber e-mails desse tópico. E ainda consto como “following”.

O problema de trocar a Slick por outra Engine, é que praticamente não existe outra engine. Daí o problema de usar Java em jogos novamente.

Além disso, é impressionante a quantidade de componentes em Java para jogos que foram desenvolvidos por um único desenvolvedor, o Kevin Glass, do site Coke and Code. Além da Slick, ele está presente em projetos como a LWJGL (base do JMonkey), JOGL, JInput, etc. O que mostra a fragilidade da plataforma Java para jogos de maneira geral.

V

Quanto a rede.

O UDP não tem conexão. Não é necessário abrir e fechar nada.

Em TCP, de maneira geral, abre-se uma conexão para a tarefa que se quer desempenhar (por exemplo, o chat) e só fecha quando essa tarefa termina. Se você controlar o jogo com TCP, irá abrir uma conexão no início do jogo, e fecha-la ao final. Existe um custo muitíssimo alto em abrir e fechar conexões…

No servidor, evite a criação de múltiplas threads. Threads não dão justiça para diferentes jogadores e sobrecarregam o servidor. Use os ByteBuffers e Selectors do java.nio, se for controlar no braço.

V

Faça a movimentação no cliente, e volte caso haja negação por parte do servidor. É melhor um pouco de “pipocation” do que lag. Qualquer tempo superior a 0,3 segundos de atraso será percebido pelo jogador e encarado como lag, caso ele pressione uma tecla e não veja a resposta.

É impressão minha ou já falei tudo isso para você em outro tópico sobre sockets e conexão?

L

Realmente java é pessimo para jogos… conheça a engine UDK 3 éla é o que á de melhor para desenvolver jogos na atualidade…

M

O servidor que estou usando e estou gostando muito é o RedDwarf, ele superou em muito minhas espectativas (Em tudo), ele basicamente cuida da parte mais problematica e me deixou livre para ver o que realmente interessa. :slight_smile:

erickzanardo

  • Me add no MSN ou Gtalk
    MSN: [email removido]
    Gtalk: [email removido]
    ps: Se tiver skype vai ser excelente “michel.montenegro”.

  • Usar Java2D puro é tenso, muito tenso, eu comecei assim no inicio e demora muito pra fazer uma besteirinha, só depois de sei lá quanto x tempo começa a melhorar, o slick 2D facilita muito deixando a parte chata para ele, o artemis é interessante por ajudar a organizar o projeto em um “world”. etc…

ViniGodoy
Então a estrtegia de movimento que comentei acima é boa? Tem algo mais que possa me falar em relação a isso que ajude?

  • Imagino que sincronizar PvP deve ser um problema, mas usando o mesmo principio da movimentação, já resolveriamos em muito isto ou me engano?
    hehehe sósobre udp e tcp falaste, o resto foi novo
    ps: muitas vezes o forum não atualiza meu email, mesmo quando marco para notificar.

luistiagos
Ainda verei o UDK 3.
Mas por hora estou com o projeto bem adiantado, e olha que estou praticamente estudando e trabalhando só nisso.

Atualmente estou com 1 questão, como vou trabalhar o sistema de batalha (Não me importo de fazer um sistema simples como o do Tibia, desde que esteja funcional), se puderem me dar uma ajuda nisso agradeço (Qualquer tipo: Conceitual ou …)
Informações:

  • Basicamente vou trabalhar só com 3 skills pilotos (1 atack fisico, 1 atack ranged (poderia ser magia ou arco, só muda a animação mesmo), 1 atack em área ).
  • Se vai ser ao estilo tátics ou tibia based, ou até outro vai ser baseado na simplicidade do mesmo (Já que estou só, prefiro fazer algo modesto que funcione e depois possa ser evoluido do que sonhar com algo que por sí só pode ser muito oneroso para um homem só rsrssr…)
V

Michel.Montenegro:
Então a estrtegia de movimento que comentei acima é boa? Tem algo mais que possa me falar em relação a isso que ajude?

  • Imagino que sincronizar PvP deve ser um problema, mas usando o mesmo principio da movimentação, já resolveriamos em muito isto ou me engano?
    hehehe sósobre udp e tcp falaste, o resto foi novo

Qual delas? Se for esperar o servidor, é péssima. Se for movimentar antes, é boa sim. Outra dica é vc pensar em animações mais longas para seus poderes. Nunca faça nada “imediato”, tipo um tipo de sniper, ou qualquer coisa nesse sentido. As animações, além de deixarem o jogo legal, servem como “enrolação” no lado do cliente para as mensagens trafegarem. A idéia é a seguinte, o comando vai ao servidor no momento que o jogador pressiona o botão de ataque/defesa. Mas aí vem a animaçãozinha do personagem, que dá tempo dos pacotes irem e voltarem.

Barras de vida relativas também ajudam no processo. Se os jogadores não puderem prever com exatidão quando um vai matar o outro, ou quanto de dano eles dão a cada golpe, você pode “segurar” um determinado jogador vivo até que a conexão do servidor confirme que ele morreu.

M
  • Vamos ver se entendi bem:

1° Caso: Movimeto.
a) Mando iniciar a animação junto com a mensagem para o servidor (Movel de oldXY(0,0) para newXY(0,32)).
b) servidor checa se a posição indicada na matriz é valida e retorna ao cliente “livre” ou “bloqueado”.
c) Se “livre” mantem a animação e prosegue com as ações, se “bloqueado” reverte a posição para oldXY(0,0).

  • Correto?

2° Caso: Enrolation.
a) o Cliente manda a mensagem informando que se movel 32pixels (Movimento de 32 em 32), isso para o servidor antecipar o movimento “final”.

b) A animação do movimento seria o enrolation, o personagem digamos leve 1 segundo para percorrer os 32px, então a mensagem para o servidor em um movimento continuo seria de 1 em 1 segundo.

  • Correto?
    Obs: Em relação ao enrolation no atack, as magias da pra gerar uma animação antes que vai garantir o enrolation, mas e nos danos fisicos como espada (Que costumam ter uma ação simples “bate”), como enrolaria ai?

3° Caso: Barras de vida: Relativa

  • Exibir as barras de HP em Porcentagem (%) seria uma saida?

  • Não exibir a HP dos inimigos ou exibir também em % resolveria isto?

  • Não exibir o dano aplicado é complicado muitas vezes o pessoal gosta de controlar a evolução do dano, como posso resolver este ponto?

4° Caso: Dano a longa distancia

  • Como seria a logica para isso? Pois o meu alvo pode ter se movido de lugar, é como a magia por exemplo teria uma delay, o alvo não estaria mais na posição inicial de onde mandei o dano. (Acho que isso se aplica até para dao corpo-a-corpo para um inimigo movél).

Agradeço a colaboração de vocês.

V

Michel.Montenegro:
a) Mando iniciar a animação junto com a mensagem para o servidor (Movel de oldXY(0,0) para newXY(0,32)).
b) servidor checa se a posição indicada na matriz é valida e retorna ao cliente “livre” ou “bloqueado”.
c) Se “livre” mantem a animação e prosegue com as ações, se “bloqueado” reverte a posição para oldXY(0,0).

Isso. Outra possibilidade é o “não” do server dizer “você deveria estar em other(x,y)”
E o cliente atualizar para essa posição.

Isso. A espada vc pode fazer o personagem saltar, girar, balançar, etc. Mas nem sempre se garante com enrolation. No caso, dê um dano pequeno com a espada e garanta com a técnica do “dano subjetivo”.

Normalmente os jogos resolvem colocando várias variáveis:

a) Barras de vida sem valores absolutos;

b) Além do dano, resistência ao dano, resistências especiais, tipos de danos e resistências combinados, etc;

c) Chances de miss e crítico;

O importate é o jogador não ter uma visão perfeitamente clara de quanto dano ele teria causado num golpe. Se ele tiver, não dá para disfarçar nunca.
Alguns servidores também transformam combos em um golpe só. Por exemplo, o jogador “A” vê uma sequencia de 3 golpes lindos em seu monitor, o B só se vê levando um pancadão só.

Experimente baixar um MMO qualquer moderno e joga-lo em duas máquinas, lado-a-lado. Facilmente vc perceberá as técnicas de dead reckoning. Se dois jogadores apostarem corrida, o avatar deles sempre estará na frente no seu próprio micro, e atrás no do adversário. Só com algum tempo de corrida é que o servidor dará a vantagem para o jogador mais rápido.

O servidor calcula a posição do alvo em algum momento. Se ele disser que acertou, a magia irá “perseguir” o jogador que levou a pancada.
Faça o teste em alguns MMOs. Você vai ver que isso acontece.

M

Entendi obrigado, conversando com uma amigo aqui, ele me falou que é comum usarem movimento do player pelo click do mouse para dar mais folego ao servidor, é mais fácil o player ficar apertando seta pra direita (Pressionando sem parar) que clicar, abordagem bem interessante, mas a movimentação pelo mouse também é importante, poderia me mandar o link de algum site que fale de como fazer o algoritimo de movimentação pelo mouse? (O personagem deve pegar sempre o melhor caminho, já levando em consideração os muros.

V

Você implementa a busca de caminhos através do algorítmo A*:
http://www.policyalmanac.org/games/aStarTutorial_port.htm

L

Eu sugiro o sistema tatics ou batalha por turno… pois este tipo de sistema aumenta a jogabilidade e é mais desafiador tendo o jogador a bolar taticas e tecnicas para derrotar o oponente e não só chegar lá e dar barrigadas nos monstros que nem no tibia este sistema classico é fraco e ultrapassado… outra vantagem seria que o “Enrolation” seria bem mais proveitoso em sistemas de batalhas deste tipo…

Não sei se vc já ouviu falar de Atlantica Online… é um rpg com ambos sistemas de turno e de tatica chamado TBS(Tatical Battle System) e é um dos melhores sistemas de batalhas de um MMORPG

S

A batalha por turno não funciona pra MMO, já pensou ter que esperar cada player fazer sua jogada? E tem um motivo pra não existir jogos blockbuster de turno, pouca gente tem paciência para este modo de jogar. Na minha opinião, perde o suspense, não tem o efeito surpresa.

L

soaresinfo:
luistiagos:

Eu sugiro o sistema tatics ou batalha por turno…

A batalha por turno não funciona pra MMO, já pensou ter que esperar cada player fazer sua jogada? E tem um motivo pra não existir jogos blockbuster de turno, pouca gente tem paciência para este modo de jogar. Na minha opinião, perde o suspense, não tem o efeito surpresa.

veja este jogo então um mmorpg de sucesso e em turno: www.playatlantica.com

em pvp este tempo sendo meio curto funciona e ainda da pra da enfase animações de skills e ataques diferentes,
e tmb tem a possibilidade de se jogar com diversos personagens com skills distintos…

E vc acha q 2 bonecos dando barrigadas tem muito suspense?

M

soaresinfo
Dofus é um MMORPG Tátics, bem popular, level up que o diga.

luistiagos
Na verdade penso em um sistema de batalha do genero. Até cogitei a ideia de fazer o modo de batalha aos moldes do Pokemon/Final fantasy, onde é um pseudo-sistema de turno, mas deve ser bem interessante. Como sou apenas um (rsrsrsr…) prefiro um sistema simples que funcione a um abusivamente complexo que vou levar decadas pra terminar só.

ViniGodoy
Obrigado! Vou estudar o link que mandaste, o que notei olhando rapido agora, é que, não tem nenhuma formula base (ex: X=Y*Z+…, onde X é a …), olhando rapido parece que ele só diz o que é conceitualmente (Mas quando ler melhor vou poder confirmar), mas com certeza deve existir uma formula pra isso.

All
Recentemente o Pedro refez o sistema de movimento, e gerou um problema, ve se derrepente podem dar uma luz. Antes quando o personagem andava a camera se movia junto com ele independente da velocidade do heroi (mover de px em px). atualmente esta dando umas tremidas na tela (Entendam tremida como a camera e o heroi não estão sincronizados (Como se o herói anda-se primeiro e depois camera ou o inverso, gerando este efeito)

  • Informações Importantes.
  1. A classe “Transform” é responsavel pela posição X e Y da ANIMAÇÃO (O metodo “addY(valor)” é só um y+=valor; o mesmo vale para o addX(…))

  2. A classe “Velocity” é responsavel pela velocidade (O metodo “getVelocity()” tem o valor de quantos pixels a animação deve se mover ).

  3. O valor de “Util.tileSize” é 32 (Todos os tiles do mapa tem o valor de 32x32)

  • Logica:
  1. Quando precionamos uma tecla (seta) a informação da posição final é enviada para o servidor para validação.
    ex: Da posição X=32/y=32 moveu-se para x=64/y=32 (Andou para a direita um tile inteiro (32pixels)), porém a animação do personagem NÃO pula 32px, ela anda de N em N pixels até chegar na posição informada (ex: “x=64/y=32”).

  2. Foi criada um classe/Thread interna “MovimentSystemThread extends Thread” dentro de “MovimentSystem”, a função dela e fazer com que cada passo do personagem tenha um delay de N milissegundos, por exemplo o personagem pode levar até 1seg para mover a sua animação por 32pixels (Então por 1seg o cliente não vai enviar a mensagem de movimento ao servidor, evitando um Flood (Excesso de mensagens).
    Obs: Ele esta fazendo isto corretamente o problema é a desincronização entre o movimento do personagem e o da camera (O maximo que consegui foi deixar mais sutil a esta desincronização, entre os dois). Lembrando que antes estava funcionando perfeitamente isso, antes era assim (E funcionava):

...
if (container.getInput().isKeyDown(Input.KEY_DOWN))
{
    transform.addY(velocity.getVelocity();
}
...

Link para a classe mencionada nos exemplos acima: MovementSystem.java

  • Vou ficar realmente grato a quem puder dar uma força, estou olhando o codigo a algumas horas e não to entendendo aonde esta o problema.
V

E não tem fórmula nenhuma mesmo. Você falou em procurar o caminho com o mouse, e deslocar-se desviando paredes. O algoritmo que te passei considera o tilemap como um grande grafo, com tiles associados a custos e traça o melhor caminho dentro desse grafo para chegar de um ponto em outro. O retorno do algorítmo seria uma lista de todos os tiles que seu agente deve percorrer. Os custos podem indicar tiles intransponíveis ou difíceis de percorrer (por exemplo, se ele se desloca 2x mais lento na lama, o custo poderia ser 2). Você também pode usar o custo para associar uma informação de perigo aos tiles, e evitar que o herói ande no meio de um mato onde pode encontrar bichos, quando tem uma estrada segura no meio.

Se você quer melhorar sua interpretação geométrica do jogo, e tratar posições e direções de uma maneira matemática mais adequada, procure estudar o conceito de vetores matemáticos: http://www.pontov.com.br/site/index.php/arquitetura/54-matematica-e-fisica/132-o-uso-de-vetores-nos-jogos

Esse conceito vai fazer com que você pare de olhar para os valores de x e y de seus agentes diretamente, e você vai conseguir analisar as movimentações simplesmente através de flechas. No artigo, há classes implementadas para vetores, embora creio que a Slick deva ter também. Sem falar que a classe encapsula operações matemáticas mais complicadas, como as envolvendo trigonometria.

Na área de matemática do portal, também uma série de artigos sobre matrizes de transformação que explicam o funcionamento matemático dos métodos translate, rotate e scale.

M

O Slick tem o “new Vector2f(float x, float y);”

Acabei de ler o 1° Link, e lendo o que escreveste agora fez sentido a questão do peso para um tile.
Mas te pergunto algo, atualmente o nosso sistema de movimento e tratamento de colisão é tratada dentro de uma matriz
ex:
-X–Y-
[0][0] = 1 //1=Muro
[0][1] = 0 //0=Livre
[1][0] = 2 //2=monstro
etc…

  • Não entendi bem a questão dos quadrados filhos, poderia comentar sobre isto?
  • Aplicar um patchFinder com o mesmo sistema de peso fica mais complexo ou mais simples quando trabalho com Matrizes sem movimento diagonal (exemplo acima)?
    Obs: Perdoe as perguntas, se forem tolas, mas por hora são minhas unicas duvidas.

Agora vou ler os outros dois links que indicaste.

V

O PathFinder vai lidar com a matriz mesmo.

Os algoritmos de pathfinding são feitos para grafos. O único detalhe é que toda matriz bidimensional pode ser enxergada como um grafo. Cada área da matriz representa um nó, e o valor do tile o custo para chegar naquele nó. Se seus tiles tem outras coisas sobre eles (paredes, monstros, etc), ao invés de usar o valor do tile diretamente, você só dá uma função que converte o objeto que está no tile para um valor (por exemplo, de acordo com uma tabela: se o tile tem floresta, retorna 3, se tem grama, 2, se tem estrada 1, se tem monstro, 5…).

M

ViniGodoy
Acabei de ler todos os links que mandaste, teve um ali que foi meio tenso (Me senti no 2° grau nas aulas de matematica, mas os exemplos com o ogro e a princesa ajudaram). Estou com dificuldade em olhar posições X e Y como flechas (A classe Point (X, Y) para mim subistituiria na hora um Vector2D(x,y) ou Vector2f(x,y). Pelo que notei a diferença é que estas duas classes possuem metodos, para tratar os vetores dentro destes conceitos.

falaste do Coke and Code e achei isto aqui sobre “pathfinding” (Exemplo em java): http://www.cokeandcode.com/pathfinding

Deixa ver se entendi bem, algumas coisas (Me corrige onde estiver errado e complementa o que faltar):

  • “Node (Nó)” neste caso seriam as 4 pontas de cada quadrado (No caso as corredenadas X,Y que iniciam cada ponta)?.

  • Peso é o grau de prioridade de um caminho sobre o outro (Ex: Area onde o personagem sofre penalidades, teriam menos peso/prioridade sobre um caminho limpo por exemplo, não excluindo este de menor prioridade, só deixando o teste neste caminho pra depois, caso os outros falhem ).

  • Grafos sao pontos que se interligam (No caso de um TiledMap, onde ó mapa é um tabuleiro (Varios quadrados), as posições X,Y de cada quadrado são os ponteiros e a distancia entre um ponto e outro seria as ligações (Setas).

  • No momento em que converti toda minha movimentação para matriz, aonde dou um valor para representar meu objeto (0=andavel, 1=muro, 2=heroi, 3=monstros, 4=outros player), passei a usar uma pratica que facilita/contribui no desenvolvimento deste algoritimo.

Sobre a questão referente ao MovimentSystem (Questão que eu não resolvi ainda):
O problema é que a informação atual do heroi é atualizada tranquilamente na classe que reinderiza o heroi, mas por algum motivo a um retardo no envio da informação para a camera (O heroi anda e para, ai X tempo depois a camera anda e para), o estranho é que ambos compartilham a classe Transform (Que tem a posição X,Y corrente do heroi).
ex:

Estou com a impressão que o problema é na Thread. Quase certo, mas não indentifiquei O problema em si. Se alguem sacar a bobagem, agradeço se comentarem.

V

Cara, o grafo é uma estrutura de dados. Você não teve isso na faculdade? Geralmente o estudamos lá.

Mas basicamente é isso. É uma lista de nós interligados entre si através de arestas.

Demora um pouco cair a ficha dos vetores, mas depois que cair, garanto que vc nunca mais vai querer olhar para coordenadas como um valor em y e outro em x.

M

Nunca tive um exemplo de grafo no desenv. de jogos, tanto que echei esquisito a ideia de grafo para algo diferente de bd ou dados vinculados. Na verdade o exemplo de grafo mais proximo que tive com este de desenv dejogos, foi quando, fizeram um grafo de um bairro e tivemos que achar a trajetorio para o GPS do veiuclo X chegar em Y. Vou dar uma boa lida e relidas na parte de vetores, até absorver este conceito bem, até “cair a ficha”.

Mas já considero isto resolvido, praticamente, sobrou apenas o problema do desinc entre a camera e o personagem, no problema que citei a uns posts acima, resolvendo ele vou implantar movimento pelo mouse. Na verdade vou fazer uma pergunta:
1-A lógica aplicada neste problema não foi errada ou foi? (Detalho ela passo a passo no post)

Mas uma coisa, mesmo o movimento sendo pelo mouse, a cada N pixels de movimento (Ex: A cada tile percorrido (32px em 32px)) tenho que validar a info no servidor pra confirmar se não houve colisão com algo (Os cliente só mandarem a informação da posição final, não invalida que a cada N tempo/Pixel movimentado não ocorra uma colisão ), ou me engano?.

V

O herói e a câmera estão em threads diferentes? Geralmente deixamos toda a lógica do jogo em uma única thread. O que jogamos para outras threads são tarefas auxiliares, como carga de cenários ou salvamento do estado do jogo, por exemplo.

Então, a idéia do PathFinding é a mesma do seu aplicativo para achar rotas no GPS. Com a diferença de que as ruas são os tiles marcados como rua. :slight_smile:
Na verdade, PathFinding é um assunto extensivamente estudado em jogos, daí os grafos. Usamos grafos também em jogos de estratégia, para representar árvores de tecnologia, requisitos de construção, etc. É bem fácil usando um grafo descobrir coisas como:
a) O que eu preciso descobrir para ter um tanque de guerra?
b) O jogador tem um tanque de guerra e um arqueiro. Quais são as descobertais mais prováveis que ele tem? E quanto em recursos ele já mineirou, no mínimo?
c) Qual é a únidade que defende tanques de guerra e qual é a rota mais barata para chegar a ela?

Algumas vezes também usamos grafos para:

a) Menus: Descobrir se nenhum menu ou grupo de menu ficou órfão;

b) Inicialização e shutdown de subsistemas: Achar a melhor sequencia de inicialização;

c) Gerencia da cena (O SceneManager é um grafo);

d) Algorítmos de jogos clássicos (bejeweled, tabuleiros, etc).

Procure blogs de inteligência artificial para jogos e você vai ver muitas técnicas sobre eles (PathFinding normal, hierarquico, hibrido, etc). Por exemplo:
http://www.ai-blog.net/archives/000152.html

É uma boa reforçar que o Morpheus estava certo no filme Matrix quando diz: “Neo, sooner or later you’re going to realize just as I did that there’s a difference between knowing the path and walking the path” (Neo, cedo ou tarde você vai descobrir, assim como eu descobri, que existe uma diferença entre saber o caminho e andar sobre o caminho).

A ciência para “saber o caminho” é o PathFinding. Agora, andar sobre o caminho é outra ciência, chamada “PathFollowing”. Para isso, existem algorítmos específicos como os steering behaviors. Você não vai precisar ir tão longe, pois seu jogo é tile based.

Quanto ao servidor, realmente, você terá que testar se no caminho não poderia ter havido uma colisão.

M

ViniGodoy:
O herói e a câmera estão em threads diferentes? Geralmente deixamos toda a lógica do jogo em uma única thread. O que jogamos para outras threads são tarefas auxiliares, como carga de cenários ou salvamento do estado do jogo, por exemplo.

Então, a idéia do PathFinding é a mesma do seu aplicativo para achar rotas no GPS. Com a diferença de que as ruas são os tiles marcados como rua. :slight_smile:
Na verdade, PathFinding é um assunto extensivamente estudado em jogos, daí os grafos. Usamos grafos também em jogos de estratégia, para representar árvores de tecnologia, requisitos de construção, etc. É bem fácil usando um grafo descobrir coisas como:
a) O que eu preciso descobrir para ter um tanque de guerra?
b) O jogador tem um tanque de guerra e um arqueiro. Quais são as descobertais mais prováveis que ele tem? E quanto em recursos ele já mineirou, no mínimo?
c) Qual é a únidade que defende tanques de guerra e qual é a rota mais barata para chegar a ela?

Algumas vezes também usamos grafos para:

a) Menus: Descobrir se nenhum menu ou grupo de menu ficou órfão;

b) Inicialização e shutdown de subsistemas: Achar a melhor sequencia de inicialização;

c) Gerencia da cena (O SceneManager é um grafo);

d) Algorítmos de jogos clássicos (bejeweled, tabuleiros, etc).

Procure blogs de inteligência artificial para jogos e você vai ver muitas técnicas sobre eles (PathFinding normal, hierarquico, hibrido, etc). Por exemplo:
http://www.ai-blog.net/archives/000152.html

É uma boa reforçar que o Morpheus estava certo no filme Matrix quando diz: “Neo, sooner or later you’re going to realize just as I did that there’s a difference between knowing the path and walking the path” (Neo, cedo ou tarde você vai descobrir, assim como eu descobri, que existe uma diferença entre saber o caminho e andar sobre o caminho).

A ciência para “saber o caminho” é o PathFinding. Agora, andar sobre o caminho é outra ciência, chamada “PathFollowing”. Para isso, existem algorítmos específicos como os steering behaviors. Você não vai precisar ir tão longe, pois seu jogo é tile based.

Quanto ao servidor, realmente, você terá que testar se no caminho não poderia ter havido uma colisão.

Vou me aprofundar nestes estudos.

Mas respondendo a pergunta a Threrad esta como uma classe interna dentro da classe principal (os metodos “process()” rodam ou dentro do metodo update ou render do jogo (Depende de cada função)).

V

Eu usaria uma thread só, para a movimentação, câmera, desenho e lógica.

M

A Camera e o redesenho do heroi deveriam ocorrer ao mesmo tempo, heroi andou, a camera acompanha (Isso funcionava, sem o menor problema). Porém quando jogo para uma Thread a função de atualizar a posição do heroi, ocorre uma falta de sincronia entre a camera e a reinderização do personagem.

Dentro do AppGameContainer (O Jogo em si), existem dois metodos (E dentro deles tem processos que ocorrem a cada atualização do render e do update):

  • update(GameContainer container, StateBasedGame sbGame, int delta)
  • world.loopStart();
  • world.setDelta(delta); //Mantem o delta atualizado dentro do mundo
  • boundarySystem.process(); //Responsavel em manter atualizado o limite do mapa para a Camera e o personagem
  • cameraSystem.process(); //Responsavel em manter a camera atualizada com o personagem
  • movementSystem.process(); //Responsavel em atualizar a posição X/Y do Personagem
  • render(GameContainer container, StateBasedGame sbGame, Graphics g)
  • renderSystem.process(); //Responsavel em desenhar tudo exceto o “UI (User Interface)”
  • renderGUISystem.process(); //Responsavel em desenhar o “UI (User Interface)”

//Quando precionamos uma tecla para o personagem andar, ele anda de N em N pixels até fechar 32pixels
//A cada N pixel, a Thread toma uma sleep de N milisegundos (Pra gerar o efeito do heroi andando e não teleportando)
MovimentSystemThread()

O Problema esta justamente quando uso a Thread acima, não entendo o porque do desenho do heroi ser atualizado, alguns instantes antes a camera.
Ex:
Heroi------>Atualiza o desenho------>Atualiza o desenho------>Atualiza o desenho …
Camera---------->Atualiza a camera-------->Atualiza o camera-------->Atualiza o camera …

Agora quando não uso a Thread, e simplesmente digo “Ao precionar a tecla X ande de N em N pixels enquanto precionar a tecla” tudo ocorre normal, mas o problema é que quero precionar a tecla, fazer a animação se mover e só re-enviar a informação pro servidor após o termino da animação.

Mas o que não entendo é o porque da Thread esta gerando uma diferença entre a camera e o redesenho do personagem, se ambos pegam a posição X e Y dele da mesma fonte (É como se a camera demora-se mais pra sair do efeito do sleep(…) ou …)

  • Log (“Origem: X/Y”)
  • Personagem esta andando de 2 em 2 pixels
  • O que estiver em negrito, esta errado (Desincronizado)

O pior que não é constante

M

Problema resolvido, Basicamente usei o seguinte algoritimo no sistema:

Se Valor XY_DO_PLAYER esta atualizado(CameraSystem e RenderSystem) então
Desenha na posição atualizada do player
Caso Contrario
Desenha na posição anterior (Que é a mesma do Camerasystem(Dessincronizado))

  • Resultado: As falhas visualmente somem.

Como esta dessincronização ocorre aleatoriamente, por algum motivo que eu não descobri, mas sei que esta vinculada a Thread (Foi a melhor solução que achei), se alguem tiver outra forma sou todo ouvidos.

Agora vou aplicar o Pathfinder no sistema, pelo click do mouse.

E

Michel.Montenegro:
Problema resolvido, Basicamente usei o seguinte algoritimo no sistema:

Se Valor XY_DO_PLAYER esta atualizado(CameraSystem e RenderSystem) então
Desenha na posição atualizada do player
Caso Contrario
Desenha na posição anterior (Que é a mesma do Camerasystem(Dessincronizado))

  • Resultado: As falhas visualmente somem.

Como esta dessincronização ocorre aleatoriamente, por algum motivo que eu não descobri, mas sei que esta vinculada a Thread (Foi a melhor solução que achei), se alguem tiver outra forma sou todo ouvidos.

Agora vou aplicar o Pathfinder no sistema, pelo click do mouse.

Fla Michel,

Estava lendo os seus posts sobre estes problema, você realmente precisa de mais de uma Thread?

Nos testes que estou fazendo pra minha engine, eu atualizo a movimentação do personagem e a camera dentro da mesma Thread, que é a própria thread principal do jogo.

[]s

M

Tentei manter tudo na mesma Thread, que é a do jogo, funcionou?
Sim! Mas com um problema, quando dava sleep( valor) ou qualquer outro meio como usando o delta com count o jogo “parava”, resultando em o personagem saltando de 32 em 32 pixels, ao invez de andar suavemente de N em N pixels até chegar em 32, ai me vi forçado a criar uma Thread para fazer isso por fora, sem dar sleep na Thread principal.

Basicamente este foi o motivo de ter criado outra thread, mas aceito sugestões para mudar isso, melhorar sempre ^^

Mas deixo claro que tentei assim nocomeço, e me vi obrigado a mudar.

E

Michel.Montenegro:
Tentei manter tudo na mesma Thread, que é a do jogo, funcionou?
Sim! Mas com um problema, quando dava sleep( valor) ou qualquer outro meio como usando o delta com count o jogo “parava”, resultando em o personagem saltando de 32 em 32 pixels, ao invez de andar suavemente de N em N pixels até chegar em 32, ai me vi forçado a criar uma Thread para fazer isso por fora, sem dar sleep na Thread principal.

Basicamente este foi o motivo de ter criado outra thread, mas aceito sugestões para mudar isso, melhorar sempre ^^

Mas deixo claro que tentei assim nocomeço, e me vi obrigado a mudar.

Então, realmente se você der um sleep na thread principal ele vai dar esse efeito, do personagem não deslizar.

O que você tem que fazer é usar o delta para calcular a quantidade de pixels que ele vai andar por atualização.

Eu brinquei um pouco com o Slick2d, e pelo que me lembro, o delta é quantos milis-segundos se passaram entre esta atualização, e a anterior, eu não vou lembrar agora exatamente qual é o cálculo, pois na minha engine eu estou fazendo um esquema diferente, mas, eu peguei um código velho meu aqui que usei o slick e é mais ou menos assim, o timePassed é o delta em questão.

// Move para a esquerada if(input.isKeyDown(Input.KEY_RIGHT)) { if(getX() < (Config.PLAYABLE_AREA - getImage().getWidth())) { setX(getX() + ((timePassed / 1000f) * 300f)); } }

Eu não lembro agora da onde eu tirei este cálculo, mas funcionou, huashusahusahusa

espero que ajude

[]s

L

:evil: :evil: :evil: :evil:

M

luistiagos:

Desenho:666/576
Camera:666/576
Desenho:666/576
Camera:666/576
Desenho:666/576

:evil: :evil: :evil: :evil:

hahahaha… que coisa terrivel, nem tinha visto. (Sai capeta)

erickzanardo:
Michel.Montenegro:
Tentei manter tudo na mesma Thread, que é a do jogo, funcionou?
Sim! Mas com um problema, quando dava sleep( valor) ou qualquer outro meio como usando o delta com count o jogo “parava”, resultando em o personagem saltando de 32 em 32 pixels, ao invez de andar suavemente de N em N pixels até chegar em 32, ai me vi forçado a criar uma Thread para fazer isso por fora, sem dar sleep na Thread principal.

Basicamente este foi o motivo de ter criado outra thread, mas aceito sugestões para mudar isso, melhorar sempre ^^

Mas deixo claro que tentei assim nocomeço, e me vi obrigado a mudar.

Então, realmente se você der um sleep na thread principal ele vai dar esse efeito, do personagem não deslizar.

O que você tem que fazer é usar o delta para calcular a quantidade de pixels que ele vai andar por atualização.

Eu brinquei um pouco com o Slick2d, e pelo que me lembro, o delta é quantos milis-segundos se passaram entre esta atualização, e a anterior, eu não vou lembrar agora exatamente qual é o cálculo, pois na minha engine eu estou fazendo um esquema diferente, mas, eu peguei um código velho meu aqui que usei o slick e é mais ou menos assim, o timePassed é o delta em questão.

// Move para a esquerada if(input.isKeyDown(Input.KEY_RIGHT)) { if(getX() < (Config.PLAYABLE_AREA - getImage().getWidth())) { setX(getX() + ((timePassed / 1000f) * 300f)); } }

Eu não lembro agora da onde eu tirei este cálculo, mas funcionou, huashusahusahusa

espero que ajude

[]s

Eu testei com delta logo de cara, mas não rolou. :frowning:

Mas vou re-tentar depois, sim, agora to aplicando pathfinder no projeto e to achando que vai sair mais rapido do que o previsto (graças ao nosso amigo do CodeAndCoke). :slight_smile:

Me fala mais sobre tua engine, o que pretende fazer?

Codigo fonte da minha classe que gerencia o movimento. Para capturar odelta só preciso usar um “world.getDelta();” em qualquer lugar do codigo abaixo.

package game.cliente.systems;

import game.cliente.components.PlayerHero;
import game.cliente.components.Transform;
import game.cliente.components.Velocity;
import game.cliente.connections.HostConnect;
import game.cliente.core.ManagerMap;
import game.cliente.utils.Util;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;

import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.EntityProcessingSystem;

/**
 * 
 * @author Michel Montenegro
 * @edited Pedro Silva Moreira - PeJuGe
 * 
 */
public class MovementSystem extends EntityProcessingSystem {
	private GameContainer container;
	private ComponentMapper<Transform> transformMapper;
	private ComponentMapper<Velocity> velocityMapper;
	private ComponentMapper<PlayerHero> playerHeroMapper;
	private HostConnect hostConnect;
	@SuppressWarnings("unused")
	private ManagerMap managerMap;
	private boolean walking = false;
	private boolean enterThread = false;
	private MovimentSystemThread movimentSystemThread;

	@SuppressWarnings("unchecked")
	public MovementSystem(GameContainer container, ManagerMap managerMap,
			HostConnect hostConnect) {
		super(Transform.class, Velocity.class, PlayerHero.class);
		this.container = container;
		this.hostConnect = hostConnect;
		this.managerMap = managerMap;
	}

	@Override
	public void initialize() {
		velocityMapper = new ComponentMapper<Velocity>(Velocity.class,
				world.getEntityManager());
		transformMapper = new ComponentMapper<Transform>(Transform.class,
				world.getEntityManager());
		playerHeroMapper = new ComponentMapper<PlayerHero>(PlayerHero.class,
				world.getEntityManager());
	}

	@Override
	protected void process(Entity e) {
		if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
			move(e, Util.KEY_ARROW_LEFT);
		} else if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
			move(e, Util.KEY_ARROW_RIGHT);
		} else if (container.getInput().isKeyDown(Input.KEY_UP)) {
			move(e, Util.KEY_ARROW_UP);
		} else if (container.getInput().isKeyDown(Input.KEY_DOWN)) {
			move(e, Util.KEY_ARROW_DOWN);
		} else if (container.getInput().isKeyDown(Input.KEY_ESCAPE)) {
			this.hostConnect.send("quit/");
		}
	}

	public void move(Entity e, int direction) {
		if (walking)
			return;

		Velocity velocity = velocityMapper.get(e);
		PlayerHero playerHero = playerHeroMapper.get(e);
		Transform t = transformMapper.get(e);

		t.setUltimaSetaPrecionada(direction);
		int x = (int) t.getX();
		int y = (int) t.getY();

		if (!ManagerMap.entityWallCollisionWith(x, y, direction)) {
			walking = true;
			if (movimentSystemThread == null) {
				movimentSystemThread = new MovimentSystemThread(t, direction,
						velocity);
				movimentSystemThread.start();
				enterThread = true;
			} else {
				movimentSystemThread.updateThread(t, direction, velocity);
				enterThread = true;
			}
			this.hostConnect.sendChannel("m/" + this.hostConnect.getLogin()
					+ "/" + playerHero.getClasseId() + "/" + direction + "/"
					+ playerHero.getName() + "/" + x + "/" + y + "/",
					Util.CHANNEL_MAP);
		}
	}

	@Override
	protected boolean checkProcessing() {
		return true;
	}

	public class MovimentSystemThread extends Thread {
		private Transform transform = null;
		private int direction;
		private Velocity velocity;

		public MovimentSystemThread(Transform ftransform, int fdirection,
				Velocity fvelocity) {
			transform = ftransform;
			transform.setLocation(transform.getX() - transform.getX()
					% Util.TILE_SIZE, transform.getY() - transform.getY()
					% Util.TILE_SIZE);

			direction = fdirection;
			velocity = fvelocity;
		}

		public void updateThread(Transform ftransform, int fdirection,
				Velocity fvelocity) {
			transform = ftransform;
			// Na criação da entidade heroi resovi isto.
			// transform.setLocation(transform.getX() - transform.getX() %
			// Util.tileSize, transform.getY() - transform.getY() %
			// Util.tileSize);

			direction = fdirection;
			velocity = fvelocity;

		}

		@Override
		public void run() {
			while (true) {
				if (enterThread == true) {
					int ftimes = Util.TILE_SIZE / velocity.getVelocity();

					do {
						switch (direction) {
						case Util.KEY_ARROW_DOWN:
							transform.addY(velocity.getVelocity());
							break;
						case Util.KEY_ARROW_UP:
							transform.addY(-velocity.getVelocity());
							break;
						case Util.KEY_ARROW_LEFT:
							transform.addX(-velocity.getVelocity());
							break;
						case Util.KEY_ARROW_RIGHT:
							transform.addX(velocity.getVelocity());
							break;
						default:
							return;
						}

						if (((direction == Util.KEY_ARROW_DOWN || direction == Util.KEY_ARROW_UP) && transform
								.getY() % Util.TILE_SIZE != 0)
								|| ((direction == Util.KEY_ARROW_LEFT || direction == Util.KEY_ARROW_RIGHT) && transform
										.getX() % Util.TILE_SIZE != 0)) {
							try {
								sleep(500 / ftimes);
							} catch (InterruptedException e) {
								System.out.println("testando.");
							}
						}
					} while (transform.getX() % Util.TILE_SIZE != transform
							.getY() % Util.TILE_SIZE);

					walking = false;
					enterThread = false;

					// Não remova, obrigatorio para manter uma estabilidade caso
					// o player fique pressionando sem parar as teclas
					try {
						sleep(500 / ftimes);
					} catch (InterruptedException e) {
						System.out.println("testando.");
					}
				}
			}
		}
	}
}
E

Estava olhando o seu código e realmente acho que se você fizer tudo numa thread só, o movimento vai fluir legal. Pq aparentemente oq está acontecendo é que a thread está tendo um intervalo linear, quando a atualização do jogo em si tem picos que variam.

Então sobre a minha engine eu tenho feito ela com base no que aprendi num curso de desenvolvimento de games que fiz pela internet, tenho usado ela como caso de estudo para conseguir entender as partes internas de um jogo e tal.

Atualmente ela tem até que bastante recursos, tem um sistema de colisão, camera com zoom, e algumas classes utilitárias, mas é mais pra estudo por enquanto msm, tem muito a melhorar =)

[]s

M

* Criei minha classe para fazer o “PathFinding”, usando os recursos do proprio Slick.

package game.cliente.pathfinder;

import game.cliente.utils.Util;

import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.pathfinding.AStarPathFinder;
import org.newdawn.slick.util.pathfinding.Mover;
import org.newdawn.slick.util.pathfinding.Path;
import org.newdawn.slick.util.pathfinding.PathFindingContext;
import org.newdawn.slick.util.pathfinding.TileBasedMap;

/**
 * @author Michel Montenegro
 */
public class PathFinder  implements TileBasedMap{
	
	private Path path;
	
	/**
	 * Guarda os locais visitados na busca
	 */
	private boolean[][] visited = new boolean[Util.WIDTH_MAP_IN_TILES][Util.HEIGHT_MAP_IN_TILES];
	
	   /**
	    * Calculate path from (sx,sy) to (ex,ey)
	    *
	    * @param sx - start x of the path
	    * @param sy - start y of the path
	    * @param ex - end x of the path
	    * @param ey - end y of the path
	    */
	   public Path updatePath(int sx, int sy, int ex, int ey) throws SlickException {
	      // find any blocked paths
	      AStarPathFinder pathfinder = new AStarPathFinder(this, 1000, false);
	      Mover dummyMover = new Mover() {};
	      resetVisited();
	      path = pathfinder.findPath(dummyMover, sx, sy, ex, ey);
	      return path;
	   }
	
	@Override
	public boolean blocked(PathFindingContext context, int x, int y) {
	    if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_GRASS){
	         return false;
	    } else if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_WALL){
	         return true;
	    } else if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_TREES){
	         return true;
	    } else if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_WATER){
	         return true;
	    } else if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_OTHER_PLAYER){
	         return false;
	    } else if (Util.OBJECTS_OF_WORLD[x][y] == Util.TERRAIN_MOB){
	         return false;
	    }
		return true;
	}

	@Override
	public float getCost(PathFindingContext context, int x, int y) {
		return 0;
	}

	@Override
	public int getHeightInTiles() {
		return Util.HEIGHT_MAP_IN_TILES;
	}

	@Override
	public int getWidthInTiles() {
		return Util.WIDTH_MAP_IN_TILES;
	}

	@Override
	public void pathFinderVisited(int x, int y) {
		visited[x][y]=true;
		
	}

	
	public void resetVisited() {
		for (int i = 0; i < Util.WIDTH_MAP_IN_TILES; i++) {
			for (int j = 0; j < Util.HEIGHT_MAP_IN_TILES; j++) {
				visited[i][j]=false;
			}
		}
	}
}

* Modo de usar:

  • Bom agora que tenho o caminho, me deem uma luz em como converter isso em um caminha suave, por exemplo, não sei se ele ta indo para baixo, cima, direita ou esquerda. Eu fiz funcionar, mas ele se teleportar por cada Tile até o local clicado, mas preciso fazer com que o personagem ande, até lá. Igual faço no movimentSystem, onde uso o teclado (Mas não queria criar outra Thread só pra isso). Alguem tem alguma ideia?
    de x em x pixels ele deve andar ante de terminar 32px ( Lembrando que cada posição do vetor ou tile é 32x32 no que se refere a animação)
M

Resolvido, na classe “MovimentSystem” adicionei um IF no final (“if (mouseClickX >= 0 && mouseClickY >= 0)”), que resolveu meu problema, como foi bem dio pelo Pejuge o metodo move, foi feito para movimentar Squares (quadrados de 32 para 32, fazendo “animação suave (Caminhar)”, só precisei detectar o sentido do movimento e informar).

@Override
	protected void process(Entity e) {
		int mouseClickX = Util.MOUSE_CLICK_POSITION_XY.x;
		int mouseClickY = Util.MOUSE_CLICK_POSITION_XY.y;

		if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
			move(e, Util.KEY_ARROW_LEFT);
		} else if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
			move(e, Util.KEY_ARROW_RIGHT);
		} else if (container.getInput().isKeyDown(Input.KEY_UP)) {
			move(e, Util.KEY_ARROW_UP);
		} else if (container.getInput().isKeyDown(Input.KEY_DOWN)) {
			move(e, Util.KEY_ARROW_DOWN);
		} else if (container.getInput().isKeyDown(Input.KEY_ESCAPE)) {
			this.hostConnect.send("quit/");
		} else if (mouseClickX >= 0 && mouseClickY >= 0) {
			Transform moviment = movimentMapper.get(e);

			PathFinder pathFind = new PathFinder();
			Path caminho;
			try {
				caminho = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho != null) {
					for (int i = 0; i < caminho.getLength(); i++) {
						int keyArrow = directionPath(moviment.getTileX(),
								moviment.getTileY(), caminho.getStep(i).getX(),
								caminho.getStep(i).getY());
						move(e, keyArrow);
					}
				}
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}
	}

Meu proximo passo é suavizar o moviment dos outrosplayers que aqui estão na base do teleporte hehehe…

E

Michel.Montenegro:
Resolvido, na classe “MovimentSystem” adicionei um IF no final (“if (mouseClickX >= 0 && mouseClickY >= 0)”), que resolveu meu problema, como foi bem dio pelo Pejuge o metodo move, foi feito para movimentar Squares (quadrados de 32 para 32, fazendo “animação suave (Caminhar)”, só precisei detectar o sentido do movimento e informar).

@Override
	protected void process(Entity e) {
		int mouseClickX = Util.MOUSE_CLICK_POSITION_XY.x;
		int mouseClickY = Util.MOUSE_CLICK_POSITION_XY.y;

		if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
			move(e, Util.KEY_ARROW_LEFT);
		} else if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
			move(e, Util.KEY_ARROW_RIGHT);
		} else if (container.getInput().isKeyDown(Input.KEY_UP)) {
			move(e, Util.KEY_ARROW_UP);
		} else if (container.getInput().isKeyDown(Input.KEY_DOWN)) {
			move(e, Util.KEY_ARROW_DOWN);
		} else if (container.getInput().isKeyDown(Input.KEY_ESCAPE)) {
			this.hostConnect.send("quit/");
		} else if (mouseClickX >= 0 && mouseClickY >= 0) {
			Transform moviment = movimentMapper.get(e);

			PathFinder pathFind = new PathFinder();
			Path caminho;
			try {
				caminho = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho != null) {
					for (int i = 0; i < caminho.getLength(); i++) {
						int keyArrow = directionPath(moviment.getTileX(),
								moviment.getTileY(), caminho.getStep(i).getX(),
								caminho.getStep(i).getY());
						move(e, keyArrow);
					}
				}
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}
	}

Meu proximo passo é suavizar o moviment dos outrosplayers que aqui estão na base do teleporte hehehe…

Fala Michel,

Uma sugestão que eu iria te dar para descobrir qual é a direção que se está andando, é usar o vetor de velocidade, se o x estiver menor que zero, esquerda, maior que zero direita, se o y maior que 0 para baixo, menor que zero para cima, levando em consideraçao que o plano é baseado em top/left.

Acho que a vantagem de se fazer assim é que o próprio player sabe qual direção ele está, independente do caminho em si.

[]s

M

erickzanardo:
Michel.Montenegro:
Resolvido, na classe “MovimentSystem” adicionei um IF no final (“if (mouseClickX >= 0 && mouseClickY >= 0)”), que resolveu meu problema, como foi bem dio pelo Pejuge o metodo move, foi feito para movimentar Squares (quadrados de 32 para 32, fazendo “animação suave (Caminhar)”, só precisei detectar o sentido do movimento e informar).

@Override
	protected void process(Entity e) {
		int mouseClickX = Util.MOUSE_CLICK_POSITION_XY.x;
		int mouseClickY = Util.MOUSE_CLICK_POSITION_XY.y;

		if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
			move(e, Util.KEY_ARROW_LEFT);
		} else if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
			move(e, Util.KEY_ARROW_RIGHT);
		} else if (container.getInput().isKeyDown(Input.KEY_UP)) {
			move(e, Util.KEY_ARROW_UP);
		} else if (container.getInput().isKeyDown(Input.KEY_DOWN)) {
			move(e, Util.KEY_ARROW_DOWN);
		} else if (container.getInput().isKeyDown(Input.KEY_ESCAPE)) {
			this.hostConnect.send("quit/");
		} else if (mouseClickX >= 0 && mouseClickY >= 0) {
			Transform moviment = movimentMapper.get(e);

			PathFinder pathFind = new PathFinder();
			Path caminho;
			try {
				caminho = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho != null) {
					for (int i = 0; i < caminho.getLength(); i++) {
						int keyArrow = directionPath(moviment.getTileX(),
								moviment.getTileY(), caminho.getStep(i).getX(),
								caminho.getStep(i).getY());
						move(e, keyArrow);
					}
				}
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}
	}

Meu proximo passo é suavizar o moviment dos outrosplayers que aqui estão na base do teleporte hehehe…

Fala Michel,

Uma sugestão que eu iria te dar para descobrir qual é a direção que se está andando, é usar o vetor de velocidade, se o x estiver menor que zero, esquerda, maior que zero direita, se o y maior que 0 para baixo, menor que zero para cima, levando em consideraçao que o plano é baseado em top/left.

Acho que a vantagem de se fazer assim é que o próprio player sabe qual direção ele está, independente do caminho em si.

[]s

Não entendi bem, de onde tiraste o valor zero para esta analise? Te lembra que o mesmo vale para o teclado, tem que se algo que se adapte aos dois tipos de movimento.

* Dica: Para movimento diagonal, só fazer assim:

  • Adicionar um atributo estatico na classe Util
  • No MovimentSystem, por um SE checando se tem duas teclas precionadas (E passar o atributo estatico correspondente no metodo “move”):
if (container.getInput().isKeyDown(Input.KEY_LEFT) && container.getInput().isKeyDown(Input.KEY_RIGHT)) {
    move(e, Util.KEY_ARROW_DIAGON_<NOME>);
  }

no metodo move e na Thread principalmente só tratar o movimento, passando tanto o valor do X como do Y que sofreram alteração ao mesmo tempo.

Como o sistema de colisão com objetos moveis como monstros ainda não esta feito, não etou usando movimentação em diagonal ( vou deixar isso para quando todo o resto esteja ok, alémdo que, eu não tenh sprites com animações em diagonal rsrsr…).

E

Então Michel,

O valor é mais ou menos assim:
As entidades dos meus jogos tem dois vetores básicos, um para a posição, e um para o movimento.

Quando o jogador aperta a tecla para cima por exemplo, eu pego o meu vetor da velocidade e somo um valor a sua variável y.

A cada atualização do jogo eu somo o vetor de valocidade com o de posição.

E deste vetor de velocidade que eu tiro os valores para a análise que te passei na outra mensagem, entedeste??
Vou ver se acho um exemplo aqui depois qualquer coisa ahuhusaushasahu

[]s

M

Tranquilo, agora estou em um dilema em relação a suavisação dos outros players na tela.
Exemplo: Esta seu personagem andando no mapa, surgem mais três players juno com você e os três começam a andar juntos, seu personagem ta otimo, andando de forma suave de N em N pixels na a nimação, porém os outros players estão se teleportando.

Agora me vem uma questão pra cada novo player, vai ter uma Thread rodando mantendo a animação dele suave, como é o caso do player principal, em tese 100 players namesma área = 100 Threads, seguindo este raciocinio, ou só uma (Neste caso uma que fique gerenciando isto.

Tentei usar o delta, mas não funcionou como deveria, nenhum pouco. Abaixo codigo com o uso do delta ( Se alguem corrigir ou me falar o que tem errado, e se funcionar migro a ideia para a suavização do player que atualmente é baseado em uma Thread externa).
Obs: No timer, coloquei 500, 1000, 2000, 5000, 10000 ( e nada …)

} else {
			int sliceX = this.oldX;
			int sliceY = this.oldY;
			do {
				int timer = 100000;
				switch (this.keyArrow) {
				case Util.KEY_ARROW_DOWN:
					sliceY += Util.SPEED_HERO;
				    g.drawAnimation(this.playerCorrente, sliceX, sliceY);
					break;
				case Util.KEY_ARROW_UP:
					sliceY -= Util.SPEED_HERO;
				    g.drawAnimation(this.playerCorrente, sliceX, sliceY);
					break;
				case Util.KEY_ARROW_LEFT:
					sliceX -= Util.SPEED_HERO;
				    g.drawAnimation(this.playerCorrente, sliceX, sliceY);
					break;
				case Util.KEY_ARROW_RIGHT:
					sliceX += Util.SPEED_HERO;
				    g.drawAnimation(this.playerCorrente, sliceX, sliceY);
					break;
				default:
					return;
				}
				if (((this.keyArrow == Util.KEY_ARROW_DOWN || this.keyArrow == Util.KEY_ARROW_UP) && sliceY % Util.TILE_SIZE != 0)
						|| ((this.keyArrow == Util.KEY_ARROW_LEFT || this.keyArrow == Util.KEY_ARROW_RIGHT) && sliceX % Util.TILE_SIZE != 0)) {
					
					while (true)
					{
						timer -=delta;
						System.out.println("wait..."+timer);
						if(timer<=0) break;
					}
				}
			} while (sliceX % Util.TILE_SIZE != sliceY % Util.TILE_SIZE);
			
			this.oldX = this.x;
			this.oldY = this.y;
		}
E

Entre ter uma thread para todos os outros players, e uma thread para cada player, eu prefiro a primeira opção.

Em relação a seu teste, acho que o problema está neste for, vou tentar organizar aqui em baixo uma lógica da movimentação.

Obs : - O delta é o tempo passado entre uma atualização e outra
- A velocidade é a quantidade de pixels que queremos que o jogador se movimente em um segundo

class Game {
   Jogador jogador;

   void update(int delta) {
       jogador.update(delta);
       // Faz o resto da logica do jogo
   }

    // outros metodos omitidos
}

class Jogador {
    Vetor posicao;
    Vetor velocidade;

    void update(int delta) {
        // Faz as verificações dos botões pressionados e etc (no caso do jogador) e atribui um valor no vetor de velocidade de acordo com o botão pressionado
        Vetor vetor = calculaProporcao(velocidade, delta);
        posicao.x += vetor.x;
        posicao.y += vetor.y;
    }

    Vetor calculaProporcao(Vetor velocidade, int delta) {
       // Faz uma regra de três para saber o quantos pixels valem em relação com o delta no nosso tempo
       // Exemplo queremos mover 32pixels por segundo (1000 milis)
       // então: 32   1000
       //         x     delta
    }
    // outros metodos omitidos
}

Não sei se deu para entender muito bem, quando eu chegar em casa eu vou baixar o slick e fazer um exemplo =)

[]s

M

erickzanardo:
Entre ter uma thread para todos os outros players, e uma thread para cada player, eu prefiro a primeira opção.

Em relação a seu teste, acho que o problema está neste for, vou tentar organizar aqui em baixo uma lógica da movimentação.

Obs : - O delta é o tempo passado entre uma atualização e outra
- A velocidade é a quantidade de pixels que queremos que o jogador se movimente em um segundo

class Game {
   Jogador jogador;

   void update(int delta) {
       jogador.update(delta);
       // Faz o resto da logica do jogo
   }

    // outros metodos omitidos
}

class Jogador {
    Vetor posicao;
    Vetor velocidade;

    void update(int delta) {
        // Faz as verificações dos botões pressionados e etc (no caso do jogador) e atribui um valor no vetor de velocidade de acordo com o botão pressionado
        Vetor vetor = calculaProporcao(velocidade, delta);
        posicao.x += vetor.x;
        posicao.y += vetor.y;
    }

    Vetor calculaProporcao(Vetor velocidade, int delta) {
       // Faz uma regra de três para saber o quantos pixels valem em relação com o delta no nosso tempo
       // Exemplo queremos mover 32pixels por segundo (1000 milis)
       // então: 32   1000
       //         x     delta
    }
    // outros metodos omitidos
}

Não sei se deu para entender muito bem, quando eu chegar em casa eu vou baixar o slick e fazer um exemplo =)

[]s

Se quiser pode até baixar o projeto e mexer diretamente nele ou algo que sirva como um exemplo generico, acabei de atualizar o projeto. ^^

Vou aguardar, porque isso afeta a animação do player, dos players da rede e dos MOBs (Monstros), etc…
Pois tentarei padronizar.

Lembre-se que:

  1. Ele se movem de N em N pixels, até completar 32pixels
    ex:
    a) 0->8->16->24->32 (de 8 em 8 pixels)
    b) 0->4->8->12->16->20->24->28->32 (de 4 em 4 pixels)
    c) 0->2->4->6->8->10->12->14->16->18->20->22->24->26->28->30->32 (de 2 em 2 pixels)
    etc…

obs: Sempro uso numeros divisiveis com 32 (1, 2, 4, 8 )

E

Fala Michel, blz?

cara fiz um exemplo bem simples pra explicar como que funciona o movimento de uma thread só, eu vou explicar abaixo o principal, mas sugiro você dar uma olhada, baixar e rodar pra ver funcionando =)
http://code.google.com/p/testes-e-exemplos/source/browse e navegue até branches>slick-walk-example
URL para fazer o checkout: http://testes-e-exemplos.googlecode.com/svn/branches/slick-walk-example

Bom, vou explicar a classe principal

Antes temos que ter um conceito de velocidade na cabeça, que é bem estupido a principio, mas é importante. A velocidade nada mais é do que um distância percorrida em um intervalo de tempo, no nosso exemplo iremos utilizar Pixels por segundo.

// Imports omitidos
public class HeroSprite extends GameSprite {

    // Vetor de posição
    private float x;
    private float y;

    // Image do spriteSheet
    private Image spriteAtual;
    private HeroPosition posicaoAtual;
    private int step;
    private int stepCount;

    public boolean moving;
    private Controller controller;

    public HeroSprite() throws SlickException {
        super("img/spriteChar1.png", 4, 4);
        // Iniciando o sprite e etc
        spriteAtual = getSheet()
                .getSprite(0, HeroPosition.FACE_DOWN.getIndex());
        posicaoAtual = HeroPosition.FACE_DOWN;
        moving = false;
    }

    public void updateHero(int delta) {
        move(delta);
    }

    private void move(int delta) {
        // Aqui você entra ou com o controlador de input do jogador, ou um path follower
        controller.control();

        if (moving) {
            // O método calcSpeed é o cara que calcula a taxa certa de pixels que o personagem deve andar
            // Os parâmetros são o delta, segundos, e pixels, o dois últimos definem a velocidade do personagem
            // no nosso caso o personagem está se movendo a 32pixels por segundo
            float speed = calcSpeed(delta, 1000f, 32f);

            // Após calcular a velocidade apenas atualiza a posição
            switch (posicaoAtual) {
            case FACE_DOWN:
                y = y + speed;
                break;
            case FACE_LEFT:
                x = x - speed;
                break;
            case FACE_RIGHT:
                x = x + speed;
                break;
            case FACE_UP:
                y = y - speed;
                break;
            }

            // Rotina para atualizar a animação
            stepCount += delta;
            if (stepCount > 250) {
                if (step == 3) {
                    step = 0;
                } else {
                    step++;
                }
                stepCount = 0;
            }
            spriteAtual = getSheet().getSprite(step, posicaoAtual.getIndex());
        }
    }

    public void setController(Controller controller) {
        this.controller = controller;
    }

    public void setMoving(boolean moving) {
        this.moving = moving;
    }

    public void setPosicaoAtual(HeroPosition position) {
        this.posicaoAtual = position;
    }

    private float calcSpeed(int delta, float time, float pixels) {
        float returnValue = 0;

        // Regra de três simples, para saber quantos pixels vale esta
        // atualização
        returnValue = (pixels * delta) / time;

        return returnValue;
    }

    public void drawHero() {
        spriteAtual.draw(x, y);
    }
}

Se você rodar o teste HeroGoToControlGameTester.java que tem na URL que te passei acima, você vai observar que existe uma retângulo vermelho, e o personagem move ao longo dele, este retângulo tem exatamente 320 pixels, se movendo a 32pixels por segundo, o personagem sai por completo do retângulo, em 10 segundos =).

No caso de você precisar fazer alguma rotina a cada 32pixels para mandar informações para o servidor, basta acumular o valor do speed em uma variável e disparar uma rotina verificando-a.

Bom basicamente é isso, qualquer dúvida estamos ai.

[]s

M

:smiley: Obrigado.
Vou estudar este codigo ainda hoje. Rapaz sumir com esta Thread vai ser um alivio. Pelo que vi ainda reaproveito o codigo para a movimentação das outras entidades do jogo, como estava querendo.

E

Michel.Montenegro:
:smiley: Obrigado.
Vou estudar este codigo ainda hoje. Rapaz sumir com esta Thread vai ser um alivio. Pelo que vi ainda reaproveito o codigo para a movimentação das outras entidades do jogo, como estava querendo.

Isso ae cara, o mesmo príncipio de movimento para uma entidade, vai ser igual para as outras, você só terá que mudar o cara que “controla” a entidade.

Qualquer dúvida cara, pergunte, e depois posta como que ficou o/

[]s

M

Consegui migrar meu MovimentSystem. :smiley:

Agora to com um problema, ve se tem como me dar alguma ideia.

  • Percebi no teu exemplo que enquanto tiver Moving TRUE ele vai andar (desde que dentrodo metodo update), porém faço teste com “Input” lógicamente ele só anda quando precioso a telca.
    ex:
if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
		Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
		moviment.setPosition(CharacterPosition.FACE_LEFT);
		moviment.setMoving(true);
		move(player,world.getDelta(), moviment, velocity);
	}

No caso acima ele sóanda quando preciono o KEY_LEFT, até ai ta otimo, só preciso que ele continue andando até fechar os 32 pixels.

  • O caso mais tenso a respeito é quando preciso fazer a checagem pelo mouse, da uma olhada nos codigos abaixo.
if (mouseClickX >= 0 && mouseClickY >= 0) {

			PathFinder pathFind = new PathFinder();
			Path caminho;
			try {
				[b]caminho[/b] = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho != null) {
					for (int i = 0; i < caminho.getLength(); i++) {
						CharacterPosition position = [b]directionPath[/b](moviment.getTileX(),
								moviment.getTileY(), caminho.getStep(i).getX(),
								caminho.getStep(i).getY());
						moviment.setPosition(position);
						moviment.setMoving(true);
						move(player,world.getDelta(), moviment, velocity);
					}
				}
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}
public CharacterPosition [b]directionPath[/b](int sX, int sY, int tX, int tY) {
		if (sX < tX) {
			return CharacterPosition.FACE_RIGHT;
		} else if (sX > tX) {
			return CharacterPosition.FACE_LEFT;
		} else if (sY < tY) {
			return CharacterPosition.FACE_DOWN;
		} else if (sY > tY) {
			return CharacterPosition.FACE_UP;
		}
		// -1 = parado
		return null;
	}

caminho é um vetor que guarda todas as cordenadas do percurso que precio percorrer parachegar aonde o mouse clicou. Em tese seria fácil, só usar o for e informa a direção que o personagem precisa seguir (recupero a direção com o “directionPath”). To achando que preciso deixar ele do lado de fora do IF, só habilitando true ou false, para mater ou não o movimento, mas o problema que no caso do click do mouse ele tem varios locais para percorrer. Me da uma luz/ideia de como poderia contornar isto, se puder.
Obs: o caminho tem as posições Xe Y em Tile e não em pixel (para ter em pixel, é só multiplicar pelo tamanho do tile, no meu caso é 32)

M

Quanto ao problema de forçar ele sempre andar 32px antes de mudar de direção ou pararfoi resolvido, agora só falta, resolver o problema do pathfinding (ou melhor como fazer ele percorrer todo o percurso indicado pra ele, antes de qualquer coisa).

M

Resolvido o problema com o pathfind, quando clico com o mouse em um ponto ele anda até lá. Só que por algum erro ( Na logica que usei, as vezes ele para 1 tile antes, do tile correto, as vezes funciona, tem algum erro ná logica usado dentro do metodo process().

package game.cliente.systems;

import game.cliente.components.Player;
import game.cliente.components.Transform;
import game.cliente.components.Velocity;
import game.cliente.connections.HostConnect;
import game.cliente.core.PathFinder;
import game.cliente.hero.CharacterPosition;
import game.cliente.utils.Util;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.pathfinding.Path;

import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.EntityProcessingSystem;

/**
 * @author Michel Montenegro
 */
public class MovementPlayerSystem extends EntityProcessingSystem {
	private GameContainer container;
	private ComponentMapper<Transform> movimentMapper;
	private ComponentMapper<Velocity> velocityMapper;
	private HostConnect hostConnect;
	private float distanceTraveled;
	private int countSteps = -1;
	private Path caminho;
	private CharacterPosition position;
	
	public MovementPlayerSystem(GameContainer container, HostConnect hostConnect) {
		super(Player.class);
		this.container = container;
		this.hostConnect = hostConnect;
	}

	@Override
	public void initialize() {
		velocityMapper = new ComponentMapper<Velocity>(Velocity.class,
				world.getEntityManager());
		movimentMapper = new ComponentMapper<Transform>(Transform.class,
				world.getEntityManager());
	}

	// // Source X = sX
	public CharacterPosition directionPath(int sX, int sY, int tX, int tY) {
		if (sX < tX) {
			return CharacterPosition.FACE_RIGHT;
		} else if (sX > tX) {
			return CharacterPosition.FACE_LEFT;
		} else if (sY < tY) {
			return CharacterPosition.FACE_DOWN;
		} else if (sY > tY) {
			return CharacterPosition.FACE_UP;
		}
		// null = parado
		return null;
	}

	@Override
	protected void process(Entity player) {
		Transform moviment = movimentMapper.get(player);
		Velocity velocity = velocityMapper.get(player);
		int mouseClickX = Util.MOUSE_CLICK_POSITION_XY.x;
		int mouseClickY = Util.MOUSE_CLICK_POSITION_XY.y;

		if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
			if (distanceTraveled == 0.0f) {
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
				countSteps=-1; caminho=null;
				moviment.setPosition(CharacterPosition.FACE_LEFT);
				moviment.setMoving(true);
			}
		} else if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
			if (distanceTraveled == 0.0f) {
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
				countSteps=-1; caminho=null;
				moviment.setPosition(CharacterPosition.FACE_RIGHT);
				moviment.setMoving(true);
			}
		} else if (container.getInput().isKeyDown(Input.KEY_UP)) {
			if (distanceTraveled == 0.0f) {
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
				countSteps=-1; caminho=null;
				moviment.setPosition(CharacterPosition.FACE_UP);
				moviment.setMoving(true);
			}
		} else if (container.getInput().isKeyDown(Input.KEY_DOWN)) {
			if (distanceTraveled == 0.0f) {
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
				countSteps=-1; caminho=null;
				moviment.setPosition(CharacterPosition.FACE_DOWN);
				moviment.setMoving(true);
			}
		} else if (container.getInput().isKeyDown(Input.KEY_ESCAPE)) {
			this.hostConnect.send("quit/");
		} else if (mouseClickX >= 0 && mouseClickY >= 0) {

			PathFinder pathFind = new PathFinder();
			try {
				caminho = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho!=null){
				System.out.println("Final PathFind: " + caminho.getStep(caminho.getLength()-1).getX()+"-"+caminho.getStep(caminho.getLength()-1).getY());
				System.out.println("Length: " + caminho.getLength());
				}
				System.out.println("Click Mouse Final: " + (mouseClickX / Util.TILE_SIZE)+"-"+(mouseClickY / Util.TILE_SIZE));
				countSteps = 0;
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}
		
		if (caminho != null && countSteps!=-1 && countSteps<caminho.getLength() && moviment.isMoving()==false) {
				position = directionPath(
						moviment.getTileX(), moviment.getTileY(),
						caminho.getStep(countSteps).getX(), caminho.getStep(countSteps)
								.getY());
				if (position!=null){
				  moviment.setPosition(position);
				  moviment.setMoving(true);
				}
				countSteps += 1;
				System.out.println(">>>>>>>>>>>>>>>>>>>>>> STEP: " + countSteps);
		} else if (caminho != null && countSteps>=caminho.getLength()) {
			countSteps=-1; 
			caminho=null;
			System.out.println(">>>>>>>>>>>>>>>>>>>>>> FIM????????");
		}
		
		move(player, world.getDelta(), moviment, velocity);
	}

	private float calcSpeed(int delta, float time, float pixels) {
		float returnValue = 0;

		// Regra de três simples, para saber quantos pixels vale esta
		// atualização
		returnValue = (pixels * delta) / time;

		return returnValue;
	}

	private void move(Entity player, int delta, Transform transform,
			Velocity velocity) {
		if (transform.isMoving()) {
			float speed = calcSpeed(delta, 1000f,
					(float) velocity.getVelocity());

			switch (transform.getPosition()) {
			case FACE_DOWN:
				transform.addY(speed);
				break;
			case FACE_LEFT:
				transform.addX(-speed);
				break;
			case FACE_RIGHT:
				transform.addX(speed);
				break;
			case FACE_UP:
				transform.addY(-speed);
				break;
			}
			System.out.println("Moveu: " + transform.getX() + "/"
					+ transform.getY());

			transform.setStepCount(transform.getStepCount() + delta);
			if (transform.getStepCount() > 1000) {
				if (transform.getStep() == 12) {
					transform.setStep(0);
				} else {
					transform.setStep(transform.getStep() + 1);
				}
				transform.setStepCount(0);
			}

			distanceTraveled += speed;
			System.out.println("Mova: " + speed + " - SpeedAcumulada: "
					+ distanceTraveled);

			if (distanceTraveled >= 32.0f) {
				distanceTraveled = 0.0f;
				System.out.println("========tileFull ZERADO! ====");
				System.out.println("=============================");
				System.out.println("=============================");
				System.out.println("=============================");
				transform.setMoving(false);

			}

			// spriteAtual = getSheet().getSprite(step,
			// posicaoAtual.getIndex());
		}
	}
	@Override
	protected boolean checkProcessing() {
		return true;
	}
}
  • Depurei o projeto, e olhem este caso estranho, onde o PathFinder (Quadrados brancos = Quadrados visitados), da o caminho correto, porém o meu personagem dobrou derrepente pro lado errado. Estou pensando se o metodo “[b]directionPath/b” não esta gerando o resultado de forma errada, mas não vejo nenhum erro neste metodo.
    Esta correta a lógica?

Uploaded with ImageShack.us

E

Michel, isto depende.

Até onde me lembro, o plano do slick2d é baseado em top left, igual ao java 2d, ou seja, um x = 2 quer dizer que será 2 pixels a mais do eixo zero, indo para a direita, a mesma coisa no y, y = 2, será dois pixels a mais do eixo 0 ou seja, indo para baixo.

A tabela fica mais ou menos assim

x > 0 = Direita
x < 0 = Esquera
y > 0 = Baixo
y < 0 = Cima

[]s

M

É isso mesmo X=Left, Top = Y; mas este não é o problema, o problema é saber a direção correta, para direcionar o movimento para o local certo. Neste caso tens que pensar em Origem e Destino:

  • Origem X é > Destino X, se sim Ir para esquerda, se for < direita;
  • Origem Y é > Destino Y, se sim Ir para cima, se for < baixo;

Após analizar exaustivamente, notei que o problema que faz todo o resto do movimento sair errado, é o 1° click do mouse (Algo no codigo abaixo faz ele parar um TILE antes, atualmente esta gerando um belo NullPointer, quando ele esta indo para o ultimo Tile).

  • Log de erro (Lembrando que o indice neste caso vai de 0 a 7) :

O problema é que ele esta repetindo o ultimo valor (X,Y) no penultimo Tile.
Mas olho e reolho o codigo e não vejo o porque dele esta repetindo o codigo.

* Screen:

  • O codigo que exibe o mapeamento do PathFind, esta no RenderSystem, mas ignore pois não influencia em nada.
    Obs: Note que o Log acima ta certinho que a imagem 3 para a direita, 1 cima, 2 direita, faltou o para cima (Mas tomei um belo null pointer e não entendo o porque. (A posição Zero é aonde esta o personagem principal).

Uploaded with ImageShack.us

  • Codigo Atual, correspondente a Screen acima:
public class MovementPlayerSystem extends EntityProcessingSystem {
	private GameContainer container;
	private ComponentMapper<Transform> movimentMapper;
	private ComponentMapper<Velocity> velocityMapper;
	private HostConnect hostConnect;
	private float distanceTraveled;
	private int countSteps = -1;
	private Path caminho;
	private CharacterPosition position;

... //Codigo que não interfere

	// // Source X = sX
	public CharacterPosition directionPath(int sX, int sY, int tX, int tY) {
		if (sX < tX) {
			return CharacterPosition.FACE_RIGHT;
		} else if (sX > tX) {
			return CharacterPosition.FACE_LEFT;
		} else if (sY < tY) {
			return CharacterPosition.FACE_DOWN;
		} else if (sY > tY) {
			return CharacterPosition.FACE_UP;
		}
		// null = parado
		return null;
	}

	@Override
	protected void process(Entity player) {
		Transform moviment = movimentMapper.get(player);
		Velocity velocity = velocityMapper.get(player);
		int mouseClickX = Util.MOUSE_CLICK_POSITION_XY.x;
		int mouseClickY = Util.MOUSE_CLICK_POSITION_XY.y;

... //Codigo que não interfere

		        if (mouseClickX >= 0 && mouseClickY >= 0) {
			PathFinder pathFind = new PathFinder();
			try {
				caminho = pathFind.updatePath(moviment.getTileX(),
						moviment.getTileY(), mouseClickX / Util.TILE_SIZE,
						mouseClickY / Util.TILE_SIZE);
				if (caminho != null) {
					countSteps = 1;
					Util.path = caminho;
					Util.visited = pathFind.getVisited();
				} else{countSteps = -1; }
				Util.MOUSE_CLICK_POSITION_XY.setLocation(-1, -1);
			} catch (SlickException e1) {
				e1.printStackTrace();
			}
		}

		if (caminho != null && countSteps != -1
				&& countSteps < caminho.getLength()
				&& moviment.isMoving() == false) {
			position = directionPath(moviment.getTileX(), moviment.getTileY(),
					caminho.getStep(countSteps).getX(),
					caminho.getStep(countSteps).getY());
			moviment.setPosition(position);
			moviment.setMoving(true);
			countSteps += 1;				
			System.out.println("caminho [Length]: " + caminho.getLength());
			System.out.println("caminho : " + caminho);
			System.out.println("Setps: " + countSteps);
			System.out.println("Position: " + position);
		} else if (caminho != null && countSteps > caminho.getLength()) {
			countSteps = -1;
			caminho = null;
			position = null;
		}

		move(player, world.getDelta(), moviment, velocity);
	}

	private float calcSpeed(int delta, float time, float pixels) {
		float returnValue = 0;

		// Regra de três simples, para saber quantos pixels vale esta
		// atualização
		returnValue = (pixels * delta) / time;

		return returnValue;
	}

	private void move(Entity player, int delta, Transform transform,
			Velocity velocity) {
		if (transform.isMoving()) {
			float speed = calcSpeed(delta, 1000f,
					(float) velocity.getVelocity());

			switch (transform.getPosition()) {
			case FACE_DOWN:
				transform.addY(speed);
				break;
			case FACE_LEFT:
				transform.addX(-speed);
				break;
			case FACE_RIGHT:
				transform.addX(speed);
				break;
			case FACE_UP:
				transform.addY(-speed);
				break;
			}
			
			distanceTraveled += speed;

			if (distanceTraveled >= 32.0f) {
				distanceTraveled = 0.0f;
				transform.setMoving(false);
			}
		}
	}

... //Codigo que não interfere

}
M

Entendi exatamente o problema:

A posição de 10/9 ele deveria ir para 11/10, mas na hora H ele vai pra 11 /9, justamente porque, após ele terminar de andar N Pixels, ao dividir este valor por 32, o resultado ao invez de dar 10 cai pra 11

justamente pq o pixel dele quando dividido por 32 da 9 e não 10

  • Tentei resolver o problema com isto :
  • Pega o resultado da divisão e soma com o resto, se for 0 = ta ok, se der 1, ele soma +1.
public int getTileX() {
		return ((int)(x/Util.TILE_SIZE)+((int)x % Util.TILE_SIZE));
	}

	public int getTileY() {
		return ((int)(y/Util.TILE_SIZE)+((int)y % Util.TILE_SIZE));
	}
  • Isso ainda não funcionou.
M

erickzanardo ou Vini
Tenho uma pergunta, no metodo move do exemplo do erickzanardo, teria como forçar ele a andar literalmente 32.0 pixels exatos ( O metodo esta perfeito, mas conforme ele anda, ele sempre da uma diferença pra mais ou menos de 0.XXXX pixels, ai uma hora a diferença faz o resultado em Tiles da errado).
ex:
Valor/32 = X tile;
Na proxima conta deveria ser X+1, mas por algum erro, ele continua em X.

Então de alguma forma tenho que fazer ele ficar certinho.

E

Michel.Montenegro:
erickzanardo ou Vini
Tenho uma pergunta, no metodo move do exemplo do erickzanardo, teria como forçar ele a andar literalmente 32.0 pixels exatos ( O metodo esta perfeito, mas conforme ele anda, ele sempre da uma diferença pra mais ou menos de 0.XXXX pixels, ai uma hora a diferença faz o resultado em Tiles da errado).
ex:
Valor/32 = X tile;
Na proxima conta deveria ser X+1, mas por algum erro, ele continua em X.

Então de alguma forma tenho que fazer ele ficar certinho.

Cara, sua dúvida não ficou muito clara.

Mas para o caso da divisão estar dando errado por causa do decimal, você pode pegar o valor, jogar num int e fazer a divisão, o número estará truncado e os decimais não irão inteferir.

Como não entendi o seu problema, não sei se isso vai ficar bonito, aliás, pensando bem é uma gambiarra
sauhsuhasauhsahuashu

Se você conseguir explicar seu problema de outra maneira talvez eu consiga te ajudar o/

[]s

M

ok, vamos por parte:

  1. O personagem esta andando, quando clico com o mouse em um local?
    Resp: Sim!

  2. Ele esta usando o PathFinder (Que informou o caminho a ser percorrido)?
    Resp: sim! e esta correto o caminho!

  3. Tem algum tratamento para fazer o movimento parar a cada 32 pixels, para sofrer nova atualização no movimento?
    Resp: Sim! “if (distanceTraveled >= 32.0f)

  4. Funciona perfeitamente? jutifique.
    Resp: Não!
    Jutificativa: Quando ele anda, o resultado depois é dividido por 32, para saber qual o Tile em que ele esta, porém tem horas que a divisão não da o tile correto.
    Exemplo:
    Steps: 6
    . De: Tile(9 /7) para Tile(9/6)
    . Andou: 32.032 px
    . Pos Final (Após ter andado): 289.69656/191.99985

Será que ele foi pro Tile 9/6?
X: 289,69656 / 32 = 9,0530175 (OK!!! Tile 9 :D)
Y: 191.99985 / 32 = 5,[telefone removido] (ERRADO!!! :frowning: )

Ao invez de ter ido para o tile Tile(9/6) ele foi para o Tile(9/5). (Se moveu 2 tiles)

Observação : 192,0 / 32 = 6

  1. Já tentaste mudar o valor do IF para “distanceTraveled >= 32.5f” por exemplo, assim ele sempre ia andar 32.0px+ com mais certeza?
    Resp: Sim já tentei! o que ocorre é que o personagem depois de um tempo, a imagem fica entre 2 Tiles (Literalmente) ai invez de estar só em um (Pois ele acaba aos poucos vançando ou recuando os 0.5 px excedente (Novamente gerando um valor errado no final.

===========================

  • Caso queira, tentar fazer isso e ver como ficaria, vou te passar o que é preciso.
  • Modo de usa-la:
  1. Use o codigo abaixo, na classe de movimento ou aonde desejar (Te retorna um Vetor com o caminho que deve ser percorrido):
PathFinder pathFind = new PathFinder();
    Path path = pathFind.updatePath(moviment.getTileX(), moviment.getTileY(), mouseClickX / Util.TILE_SIZE, mouseClickY / Util.TILE_SIZE);

index = 0;
x= path.getStep(index).getX();
y= path.getStep(index).getY();
  1. Copie esta classe pra ti: Util
    Obs: O pathFinder usa ela.

2.1 Para popular o “Util.OBJECTS_OF_WORLD”, coloque o metodo abaixo em qualquer lugar (ao inicializar o jogo), use com o caminho livre mesmo, como esta abaixo, depois coloca os obstaculos no vetor se quiser.

private void initMapWall() {
		Util.OBJECTS_OF_WORLD = new int[50][50];

		for (int x = 0; x < Util.OBJECTS_OF_WORLD.length; x++) {
			for (int y = 0; y < Util.OBJECTS_OF_WORLD[0].length; y++) {
				Util.OBJECTS_OF_WORLD[x][y] = Util.TERRAIN_GRASS; //Terreno livre
				//Caso queira adicionar um muro no vetor use = Util.TERRAIN_WALL 
			}
		}
	}
  1. Agora tenta aplicar a movimentação pelo mouse com um caminho predefinido.

Nota: Pode testar diretamente no JMMORPG, caso ache melhor.
Link: [Tutorial] Instalando o projeto JMMORPG

Quem olha deve ta pensando “ainda não resolveu?”, é ainda não, foi chato pra descobrir a origem real do problema e até o momento não me veio o “estalo” com a solução.
U.U Falta tão pouco e morrer na beira não cola rsrsrsrs…

E

Olha, eu acho que você consegue resolver isso facilmente usando arredondamento, ai seu 191.99985 vira 192 que dividido por 32 da o seis, e o 289,69656 vira 290 e continua dando 9 e pouco.

Acredito que você já conheça a classe Math, mas ai vai a documentação
http://download.oracle.com/javase/6/docs/api/java/lang/Math.html

Acho fazendo Math.round(289,69656) / 32, seu problema será resolvido,
de qualquer maneira se não der certo avisa, que quando eu estiver em casa eu tento fazer o esquema do pathfind

[]s

M

erickzanardo:

Será que ele foi pro Tile 9/6?
X: 289,69656 / 32 = 9,0530175 (OK!!! Tile 9 )
Y: 191.99985 / 32 = 5,[telefone removido] (ERRADO!!! )

Ao invez de ter ido para o tile Tile(9/6) ele foi para o Tile(9/5). (Se moveu 2 tiles)

Observação : 192,0 / 32 = 6

Olha, eu acho que você consegue resolver isso facilmente usando arredondamento, ai seu 191.99985 vira 192 que dividido por 32 da o seis, e o 289,69656 vira 290 e continua dando 9 e pouco.

Acredito que você já conheça a classe Math, mas ai vai a documentação
http://download.oracle.com/javase/6/docs/api/java/lang/Math.html

Acho fazendo Math.round(289,69656) / 32, seu problema será resolvido,
de qualquer maneira se não der certo avisa, que quando eu estiver em casa eu tento fazer o esquema do pathfind

[]s

Usei o Math, mas ainda tive problema depois de X tempo andando ( não entendi o porque), mas melhorou MUITO, quando fiz isso. Então juntei isto ao que tinha feito antes, e ficou perfeito, aparentemente esta 100% agora.

O que eu fiz, após andar 32px eu literalmente movo o personagem para a posição, pixel em que ele deveria estar para que o proximo movimento fique perfeito. Isso me gerava alguns problemas, quando apliquei o “Math.round(…);”, pronto, resolveu! Não vejo mais ele dando pulos, paradas indevidas, ou qualquer coisa do genero.
^^

Muito obrigado! Sua ajuda foi belissima neste caso, mas juro que grito (Help) se tiver algum problema no movimento dele ainda.
rsrsrs…

Mas esta perfeito.
Esta PERFEITO mesmo! até quando clico que nem louco mudando de direção, to tentando forçar um erro, mas ele ta respondendo exatamente como tem que responder.

erickzanardo vais ganhar um reconhecimento direto na classe MovimentSystem, pois foste basicamente o responsavel pelo movimento esta funcionando ( eu só interpretei o que disseste e teus exemplos, mas o reconhecimento vou dar ^^ )

/**
 * @author Michel Montenegro
 * - Criador do 1° sistema de movimento.
 * 
 * @Agradecimentos
 * - Pedro Silva Moreira [PeJuGe]: Por ter desenvolvido a 2ª versão do sistema de movimento, usando uma Thread externa.
 * - Erickzanardo: Por ter sido o meu co-desenvolvedor na 3ª versão do sistema de movimento atual. 
 */

Caso queira o nome e sobre nome só informar que ponho. :slight_smile:

E

Que bom que deu certo Michel,

Se precisar de ajudar em outra coisa avisa, que se eu souber eu ajudo.

E agradeço pelo reconhecimento :smiley:

[]s

M

Tranquilo :slight_smile:

Rapaz agora vou ver como faço pra suavizar o movimento dos players da rede, ao invez de teleportarem. Com o conceito já usado no movimento do heroi acredito que isto acabei sendo tranquilo, mas vou ver isso, agor vou ver qual a melhor maneira de tratar varias entidades do mesmo tipo no Artemis, justmente para fazer o movimento suave destas antidades, lembrando que são varias entidades para ser gerenciada pelo metodo move.

M

Preciso de uma ideia. Mas antes vamos ao ambiente:

  1. Cada novo player logado envia suas coordenadas para os outros players!

  2. As informações enviadas são recebidas e guardadas localmente!

  3. Cada Player da rede é guardado como uma Entidade (Logo possui componentes (Velocity, Transform, etc…))!

  4. Faço um laço aonde pego todas as entidades do grupo (“Group: PlayerNetWork”) e mando desenhar na tela!

  5. Como funciona o “item 4”?

5.1 Quando o personagem tem seu movimento validado (não colide com nada), ele envia para os clientes a sua posição final.
ex:

  • É enviado o tile que ele deve estar após o movimento (“tile origem 5” -> “tile destino 6”), é enviado “tile 6” (sua posição futura).

5.2 O personagem inicia uma movimentação suave (anda até o tile indicado). Porém seu personagem que é exibido para os outros players se teleporta para a posição futura (Isto estaria correto se não fosse o teleporte).

  • Me deem uma ideia de como poderia usar o metodo move indicado aqui, de uma forma que funciona-se, para as entidades que representam os outros players (Lembrando que para reinderiza-las uso um for, porém cada uma delas tem seus componentes como o transform que possui sua posição X, Y (Inclusive se alterar este valor altera diretamente a posição dele na tela, tanto que eles se teleportam corretamente, só falta suavizar) ).
M

Resolvido!

A solução foi bem simples, tanto a entidade Player como o PlayerNetwork, possuem componentes comuns:

  • Velocity, responsavel pelo(a):
    . Velocidade da animação
    . Angulo da animação.

  • Transform, responsavel pelo(a):
    . PosiçãoXY atual
    . PosiçãoXY futura
    . Movimento (Parado ou andando)
    . Distancia percorrida (Enquanto andou e zerada ao estar parado)
    . Posição da Animação (Cima, Baixo, Esquerda, Direita) )

erickzanardo com certeza deve ter notado que no Transform tem basicamente os atributos necessarios para o metodo move, e foi exatamente isto que fiz, deixei que o gerenciamento da posição futura, se esta ou não andando, distancia que já percorreu enquanto moviment=true e a posição da animação na responsabilidade da propria entidade (atravez de seu component Transform). Então criei outra classe “MovimentPlayerNetWorkSystem” para gerenciar só o movimento dos players da rede (Veja o codigo abaixo).

@Override
	protected void processEntities(ImmutableBag<Entity> playersNetwork) {
		
		if (playersNetwork!=null){
			for (int i = 0; i < playersNetwork.size(); i++) {
				Entity playerNetwork = playersNetwork.get(i);
				Transform transform = transformMapper.get(playerNetwork);
				Velocity velocity = velocityMapper.get(playerNetwork);
				move(playerNetwork, world.getDelta(), transform, velocity);
			}
		}
	}

	private float calcSpeed(int delta, float time, float pixels) {
		float returnValue = 0;

		returnValue = (pixels * delta) / time;

		return returnValue;
	}

	private void move(Entity player, int delta, Transform transform,
			Velocity velocity) {
		if (transform.isMoving()) {
			float speed = calcSpeed(delta, 1000f,
					(float) velocity.getVelocity());
			// speed = (int)speed;
			switch (transform.getPosition()) {
			case FACE_DOWN:
				transform.addY(speed);
				break;
			case FACE_LEFT:
				transform.addX(-speed);
				break;
			case FACE_RIGHT:
				transform.addX(speed);
				break;
			case FACE_UP:
				transform.addY(-speed);
				break;
			}

			transform.setDistanceTraveled(transform.getDistanceTraveled() + speed);

			if (Math.round(transform.getDistanceTraveled()) >= Util.TILE_SIZE) {
				if ((transform.getFuturePositionTileX() != Util.NO_CLICK_OR_PRESS_KEY
						&& transform.getFuturePositionTileY() != Util.NO_CLICK_OR_PRESS_KEY)) {
					transform.setLocation(transform.getFuturePositionTileX()
							* Util.TILE_SIZE, transform.getFuturePositionTileY()
							* Util.TILE_SIZE);
				}
				transform.setMoving(false);
				transform.setFuturePositionTileX(Util.NO_CLICK_OR_PRESS_KEY);
				transform.setFuturePositionTileY(Util.NO_CLICK_OR_PRESS_KEY);
				transform.setDistanceTraveled(0.0f);

			}
		}
	}

Observação: O codigo que cria/atualiza/remove as entidades relacionadas aos outros players, fica na classe InGameState. (Caso alguem queira dar uma olhada).

  • Pronto esta funcionando perfeitamente agora. :slight_smile:

Acredito que em relação a movimentação das outras criaturas da tela, devo manter omesmo principio.

T

Michel, em primeiro lugar parabéns pela iniciativa e pelo esforço em criar esse motor de jogo.

Mas deixa eu te perguntar uma coisa: você pretende que este projeto seja de código aberto, que aceite contribuições de outros desenvolvedores? Eu mesmo, que estudo desenvolvimento e projeto de jogos, gostaria de contribuir.

Se for assim, eu sugiro que você hospede o código no GitHub. Lá fica mais fácil um desenvolvedor fazer fork do projeto e contribuir de forma colaborativa.

M

Sim o projeto e de codigo aberto, meu intuito principal e fazer deste projeto algo academico e que possa ser explorado por terceiros. Sinceramente nunca usei o “GitHub” não faço nem ideia dos facilitadores ou complicadores dele.

Inclusive minha monografia vai ser sobre desenvolvimento de jogos, devo começar esta semana.

A parte do projeto que gostaria de ajuda forte é no GUI, atualmente os dois melhores são nifty-gui e o twl (Optei pelo nifty-gui, por também poder ser usado no JMonkeyEngine, se olhar na screen até já foi feito algo, mas preciso de apoio nesta parte do projeto a priori). Assim que conseguir respirar em relação as provas que to fazendo, pretendo terminar de implanatar o Spawn mob.

M

Estou prestes a retomar o projeto novamente, ontem a noite revisei todo o projeto e vi que a pendencia seria no GUI (Interface grafica: botões de skill, etc.)

Estava usando o nifty-gui, pensei em mudar para o TWL. Mas existe a solução que inclusive o Bruno me aconselhou e depois de conversarmos uma noite achamos interessante usarmos imagens em uma camada acima das outras e só manipular a posição do mouse para saber se clicamos em um “botão” (2 Imagens que se alternam Click\NoClick por exemplo).

Sobre isso qual a estrategia que vocês me aconselhariam a seguir?

M

Estou com um problema quem puder ajudar ficarei grato.

Resolvi testar como ficaria usando um GUI Swing então fiz o seguinte teste:

  1. Coloquei o jogo dentro de um JPAINEL.
  2. Adicionei um jEdtText, jButton e jToggleButton.
  3. Ao escrever algo no jEdtText, ele automaticamente escreve DENTRO DO JOGO.
  4. Ao clicar no jToggleButton (Checked), ao clicar com o mouse ele vai pondo imagens (E criando entidades) estilo um Tower Defense.
  5. Ao clicar em jButton ele só fecha o jogo.

Obs: fiz isso somente para testar.

Curiosidade: O Jogo esta literalmente dentro do Painel, inclusive o GUI do Nifty esta funcionando perfeitamente dentro dele.

Vamos ao problema apesar de tudo ter funcionado perfeitamente, algo não ficou perfeito, quando eu tiro o foco do container ele “congela” a imagem, ele NÃO para o processamento, se eu mando andar e clico no jEdit, ele pausa a imagem, mas quando devolvo o foco ele ta lá aonde deveria estar (ainda andando ou já no local), até meus amigos na rede continuam recebendo o resultado como se tivessenormal ( E esta), ele só não esta atualizando. Não sei como resolver isso e já ppassei uns dias pesquisando, se alguem tiver alguma solução ou ideia ia agradecer muito, pois fora isso, posso voltar a pensar no projeto (O Jogo em si).

* Imagem da tela quando estava testando:

Uploaded with ImageShack.us

M

Novas Screens do Projeto:






L

Eu sou da época que homens, eram homens e desenvolviam seus próprios Servidores.

Mano, tu já fez a engine no braço.

Faz o logo o servidor, ai tu já deixa otimizado do seu jeito.

Esse negocio de ficar pegando as coisas prontas é bom pra estudar,
Agora implementa o seu, precisar de ajuda com TCP,Socket, qlqr coisa tamo junto!

Abraço!

M

O problema é que só posso me focar em um alvo por vez, se não nada fica pronto. Graças a minha organização o projeto não morreu, inclusive esta atualizado com tecnologias recentes. Eu já terminei a versão alpha, mas só devo lançar em fevereiro, pois tenho que fazer o manual em pdf e o vídeo ensinando a usar.

Mas fazer o servidor no braço, não é uma ideia descartada, isso vai depender de N fatores, mas o meu ponto de partida é performance e resultados.

Criado 19 de junho de 2011
Ultima resposta 15 de jan. de 2014
Respostas 89
Participantes 11