[SourceForge] JMMORPG Project

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

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

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

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;
		}

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

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

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 )

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

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

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

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

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)

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

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

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

É 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

}

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.

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.

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

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

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,9999953125 (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…

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

[quote=erickzanardo][quote]
Será que ele foi pro Tile 9/6?
X: 289,69656 / 32 = 9,0530175 (OK!!! Tile 9 )
Y: 191.99985 / 32 = 5,9999953125 (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
[/quote]

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

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:

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