[SourceForge] JMMORPG Project

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.

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.

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…).

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.

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.

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?.

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.

[quote=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.[/quote]

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)).

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

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

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.

[quote=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.
[/quote]

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

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.

[quote=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.[/quote]

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

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

[quote=luistiagos][quote]
Desenho:666/576
Camera:666/576
Desenho:666/576
Camera:666/576
Desenho:666/576
[/quote]

:evil: :evil: :evil: :evil: [/quote]

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

[quote=erickzanardo][quote=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.[/quote]

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

[/quote]

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.");
					}
				}
			}
		}
	}
}

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

* 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)

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…

[quote=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…[/quote]

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