Jump to content
  • Advertisement
Sign in to follow this  
tom_mai78101

[Solved] Need tips on Pokémon character turning/facing: Tap D-pad to turn, Press D-pad to move.

This topic is 2053 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

You know that in Pokémon Gold, Silver and Crystal, when you tap on the D-pad, you turn your character towards the general direction, or in other words, you just simply change the facing value.

 

Currently, I have the movement implemented correctly, and I'm tackling the turning/facing problem, only to fail at it.

 

This is the movement code that is run at each game tick moment:

 

	@Override
	public void tick() {
		if (!lockTurning) {
			if (keys.up.isDown) {
				turning = 2;
				lockTurning = true;
			}
			else if (keys.down.isDown) {
				turning = 0;
				lockTurning = true;
			}
			else if (keys.left.isDown) {
				turning = 1;
				lockTurning = true;
			}
			else if (keys.right.isDown) {
				turning = 3;
				lockTurning = true;
			}
		}
		if (!lockWalking && turningTick < 0) {
			if (keys.up.isDown) {
				facing = 2;
				yAccel--;
				lockWalking = true;
			}
			else if (keys.down.isDown) {
				facing = 0;
				yAccel++;
				lockWalking = true;
			}
			else if (keys.left.isDown) {
				facing = 1;
				xAccel--;
				lockWalking = true;
			}
			else if (keys.right.isDown) {
				facing = 3;
				xAccel++;
				lockWalking = true;
			}
			if (turningTick < -5)
				lockTurning = false;
		}
		handleMovement();
		turningTick--;
	}

As you can see, I put a lockdown on the facing and turning. Unfortunately, since the game is running at 60 game ticks per second, it's too fast to register a turn and not make the character move.

 

I was wondering how I should accomplish this (just turning, yet only move when a player presses down the D-pad)?

 

This is how I handle the character's movement:

 

	private void handleMovement() {
		if (lockTurning) {
			facing = turning;
		}
		if (lockWalking) {
			xPosition += xAccel;
			yPosition += yAccel;
		}
		if (xPosition % 16 == 0 && yPosition % 16 == 0) {
			lockWalking = false;
			xAccel = 0;
			yAccel = 0;
		}
	}

This is how I handle the rendering portion of my character:

 

	@Override
	public void render(BaseScreen screen) {
		screen.blit(Art.player[facing][0], xPosition, yPosition, 16, 16);
	}

 

And this is the components of a game tick:

 

	@Override
	public void run() {
		long lastTime = System.nanoTime();
		long lastTimer = System.currentTimeMillis();
		double unprocessed = 0.0;
		int frames = 0;
		int tick = 0;
		try {
			init();
		}
		catch (Exception e) {
			e.printStackTrace();
			return;
		}
		
		while (running) {
			double nsPerTick = 1000000000.0 / 60.0;
			boolean shouldRender = false;
			while (unprocessed >= 1) {
				tick++;
				unprocessed -= 1;
			}
			
			//Limits the number of ticks per unprocessed frame.
			int toTick = tick;
			if (tick > 0 && tick < 3)
				toTick = 1;
			if (tick > 20)
				toTick = 20;
			
			for (int i = 0; i < toTick; i++) {
				tick--;
				tick();
				shouldRender = true;
			}
			
			BufferStrategy bufferStrategy = this.getBufferStrategy();
			if (bufferStrategy == null) {
				this.createBufferStrategy(4);
				continue;
			}
			
			if (shouldRender) {
				frames++;
				Graphics g = bufferStrategy.getDrawGraphics();
				render(g);
				g.dispose();
			}
			
			long now = System.nanoTime();
			unprocessed += (now - lastTime) / nsPerTick;
			lastTime = now;
			
			try {
				Thread.sleep(1);
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			if (shouldRender) {
				if (bufferStrategy != null) {
					bufferStrategy.show();
				}
			}
			
			if (System.currentTimeMillis() - lastTimer > 1000) {
				lastTimer += 1000;
				fps = frames;
				System.out.println("FPS: " + Integer.toString(fps));
				frames = 0;
			}
		}
		//Game loop has exited, everything stops from here on out.
	}

I know it's hacky, but it just works. Now I feel dumb when I realized I don't have the design fully planned out.

Share this post


Link to post
Share on other sites
Advertisement

Convert your directional input to a symbol (or symbolic number) indicating one direction. Compare that to your current facing-direction. If they match then move. If not then change facing. If it's too twitchy then you can either slow down the simulation a bit (for this type of game) or else just have a variable that stores a thresh-hold before triggering movement and increment it while the direction is held until it meets the required value.
 

/* CRAPPY PSEUDOCODE */

enum Direction {
  NO_DIR = 0,
  SOUTH,
  WEST,
  EAST,
  NORTH
};

Direction dir4() { //member of input handler
  if(this.up.buttonDown)    {return NORTH;}
  if(this.down.buttonDown)  {return SOUTH;}
  if(this.left.buttonDown)  {return WEST;}
  if(this.right.buttonDown) {return EAST;}
  return NO_DIR;
}

void processPlayerInput() { //member of player
  if(this.motionFrames > 0) {
    //advance animation by one step in direction of facing
  }
  else {
    Direction moveDir = dir4();
    if(moveDir == this.faceDir) {
      this.motionFrames = TILE_MOVE_MOTION_FRAMES;
    }
    else {
      this.faceDir = moveDir;
    }
  }
}
Edited by Khatharr

Share this post


Link to post
Share on other sites

You need another condition(it can be a boolean or a string) to test for the state of direction your character is facing. 

 

Also avoid magic numbers. The code can get buggy easily with those magic numbers laying around.

Edited by warnexus

Share this post


Link to post
Share on other sites

The methods mentioned above are completely fine and are probably better than what I'm about to suggest, but if you want something more simplistic...

 

Make a "turn button" where, when held, your character turns freely but will not move.  The player knows that so long as he/she is holding the button, they will not move.

 

Just a suggestion, but I've seen that in games before, typically rogue-likes where a mistaken move when you only meant to turn can mean defeat.

Share this post


Link to post
Share on other sites

From the feedbacks received, I've decided to rewrite the code. Right now, I'm in class posting this (we're on break, so... free time!)

 

Here's the rewritten portion given below:

 

Main class: A small framework that contains an AWT component, all in the name of trying to make the inputs work as intended. Note, that I've made it so that there are 60 ticks per second.

 

package keys;

import java.awt.Canvas;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main extends Canvas implements Runnable {
	public boolean running;
	private Input input;
	public Keys keys = new Keys();
	
	public static void main(String[] arg) {
		Main main = new Main();
		JFrame frame = new JFrame("Test");
		JPanel panel = new JPanel();
		panel.add(main);
		frame.setContentPane(panel);
		frame.pack();
		frame.setSize(new Dimension(640, 480));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
		
		main.setFocusable(true);
		main.requestFocusInWindow();
		
		main.start();
	}
	
	public synchronized void start() {
		running = true;
		Thread thread = new Thread(this);
		thread.start();
	}
	
	public void initInput() {
		input = new Input(keys);
		addKeyListener(input);
	}
	
	@Override
	public void run() {
		double unprocessed = 0.0;
		int frames = 0;
		long lastConsoleTimer = System.currentTimeMillis();
		final double NanoPerTick = 1000000000.0 / 60.0;
		long lastTime = 0;
		long now = System.nanoTime();
		
		try {
			initInput();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		
		while (running) {
			lastTime = now;
			now = System.nanoTime();
			unprocessed += (now - lastTime) / NanoPerTick;
			boolean shouldRender = false;
			while (unprocessed > 1) {
				unprocessed -= 1;
				tick();
				frames++;
				shouldRender = true;
			}
			
			if (shouldRender) {
				render();
			}
			
			if (System.currentTimeMillis() - lastConsoleTimer > 1000) {
				lastConsoleTimer += 1000;
				System.out.println("TICKS: " + frames);
				frames = 0;
			}
		}
	}
	
	public void tick() {
		keys.tick();
		if (keys.up.tappedDown)
			System.out.println("Up is tapped.");
		if (keys.up.pressedDown)
			System.out.println("Up is pressed.");
		if (keys.down.tappedDown)
			System.out.println("Down is tapped.");
		if (keys.down.pressedDown)
			System.out.println("Down is pressed.");
		if (keys.left.tappedDown)
			System.out.println("Left is tapped.");
		if (keys.left.pressedDown)
			System.out.println("Left is pressed.");
		if (keys.right.tappedDown)
			System.out.println("Right is tapped.");
		if (keys.right.pressedDown)
			System.out.println("Right is pressed.");
	}
	
	public void render() {
		
	}
}

 

Keys class: An object that holds all mapped input keys.

 

package keys;

import java.util.ArrayList;
import java.util.List;

public class Keys {
	public class Key {
		public final String name;
		public boolean isTapped;
		
		public boolean tappedDown;
		public boolean pressedDown;
		
		public boolean nextState;
		
		public Key(String name) {
			this.name = name;
			all.add(this);
		}
		
		public void tick() {
			if (isTapped) {
				tappedDown = nextState;
				pressedDown = false;
			}
			else {
				pressedDown = nextState;
				tappedDown = false;
			}
		}
		
		public void release() {
			nextState = false;
		}
	}
	
	private List<Key> all = new ArrayList<Key>();
	
	public Key up = new Key("up");
	public Key down = new Key("down");
	public Key left = new Key("left");
	public Key right = new Key("right");
	
	public void tick() {
		for (Key k : all)
			k.tick();
	}
	
	public void release() {
		for (Key k : all)
			k.release();
	}
}

 

Input class: The input handler class, for use with the KeyListener in the AWT components.

 

package keys;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import keys.Keys.Key;

public class Input implements KeyListener {
	
	private int tick = 0;
	private Map<Key, Integer> mappings = new HashMap<Key, Integer>();
	
	public Input(Keys keys) {
		initKey(keys.up, KeyEvent.VK_UP);
		initKey(keys.down, KeyEvent.VK_DOWN);
		initKey(keys.left, KeyEvent.VK_LEFT);
		initKey(keys.right, KeyEvent.VK_RIGHT);
	}
	
	@Override
	public void keyPressed(KeyEvent event) {
		toggle(event, true);
		tick++;
		if (tick > 10)
			tick = 10;
	}
	
	@Override
	public void keyReleased(KeyEvent event) {
		toggle(event, false);
		tick = 0;
	}
	
	@Override
	public void keyTyped(KeyEvent event) {
	}
	
	private void toggle(KeyEvent event, boolean value) {
		Key key = null;
		Set<Key> keySet = mappings.keySet();
		for (Key k : keySet)
			if (mappings.get(k) == event.getKeyCode())
				key = k;
		if (key != null) {
			if (tick > 0) {
				key.isTapped = false;
				key.nextState = value;
			}
			else {
				key.isTapped = true;
				key.nextState = value;
			}
		}
	}
	
	private void initKey(Key key, int defaultKeyCode) {
		mappings.put(key, defaultKeyCode);
	}
}

 

I am able to separate when the key pressed is actually a tap, or a press. Thanks to the amount of ticks per second that were being counted, 1 single tick = 1/6 seconds, which is enough for a program to differeniate between a person "tapping" or "pressing". These are determined by detecting the boolean values, "tappedDown" and "pressedDown". They are used in the Main class.

 

I think this method works efficiently. Please give feedbacks, always appreciated.

 

Now, on towards implementing sprite arts and animations.

Edited by tom_mai78101

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!