242 lines
8.2 KiB
Python
242 lines
8.2 KiB
Python
"""
|
|
McRogueFace Tutorial - Part 2: Enhanced with Single Move Queue
|
|
|
|
This tutorial builds on Part 2 by adding:
|
|
- Single queued move system for responsive input
|
|
- Debug display showing position and queue status
|
|
- Smooth continuous movement when keys are held
|
|
- Animation callbacks to prevent race conditions
|
|
"""
|
|
import mcrfpy
|
|
import random
|
|
|
|
# Create and activate a new scene
|
|
mcrfpy.createScene("tutorial")
|
|
mcrfpy.setScene("tutorial")
|
|
|
|
# Load the texture (4x3 tiles, 64x48 pixels total, 16x16 per tile)
|
|
texture = mcrfpy.Texture("assets/tutorial2.png", 16, 16)
|
|
|
|
# Load the hero sprite texture (32x32 sprite sheet)
|
|
hero_texture = mcrfpy.Texture("assets/custom_player.png", 16, 16)
|
|
|
|
# Create a grid of tiles
|
|
# Each tile is 16x16 pixels, so with 3x zoom: 16*3 = 48 pixels per tile
|
|
|
|
grid_width, grid_height = 25, 20 # width, height in number of tiles
|
|
|
|
# calculating the size in pixels to fit the entire grid on-screen
|
|
zoom = 2.0
|
|
grid_size = grid_width * zoom * 16, grid_height * zoom * 16
|
|
|
|
# calculating the position to center the grid on the screen - assuming default 1024x768 resolution
|
|
grid_position = (1024 - grid_size[0]) / 2, (768 - grid_size[1]) / 2
|
|
|
|
grid = mcrfpy.Grid(
|
|
pos=grid_position,
|
|
grid_size=(grid_width, grid_height),
|
|
texture=texture,
|
|
size=grid_size, # height and width on screen
|
|
)
|
|
|
|
grid.zoom = 3.0 # we're not using the zoom variable! It's going to be really big!
|
|
|
|
# Define tile types
|
|
FLOOR_TILES = [0, 1, 2, 4, 5, 6, 8, 9, 10]
|
|
WALL_TILES = [3, 7, 11]
|
|
|
|
# Fill the grid with a simple pattern
|
|
for y in range(grid_height):
|
|
for x in range(grid_width):
|
|
# Create walls around the edges
|
|
if x == 0 or x == grid_width-1 or y == 0 or y == grid_height-1:
|
|
tile_index = random.choice(WALL_TILES)
|
|
else:
|
|
# Fill interior with floor tiles
|
|
tile_index = random.choice(FLOOR_TILES)
|
|
|
|
# Set the tile at this position
|
|
point = grid.at(x, y)
|
|
if point:
|
|
point.tilesprite = tile_index
|
|
|
|
# Add the grid to the scene
|
|
mcrfpy.sceneUI("tutorial").append(grid)
|
|
|
|
# Create a player entity at position (4, 4)
|
|
player = mcrfpy.Entity(
|
|
(4, 4), # Entity positions are tile coordinates
|
|
texture=hero_texture,
|
|
sprite_index=0 # Use the first sprite in the texture
|
|
)
|
|
|
|
# Add the player entity to the grid
|
|
grid.entities.append(player)
|
|
grid.center = (player.x + 0.5) * 16, (player.y + 0.5) * 16 # grid center is in texture/pixel coordinates
|
|
|
|
# Movement state tracking
|
|
is_moving = False
|
|
move_queue = [] # List to store queued moves (max 1 item)
|
|
#last_position = (4, 4) # Track last position
|
|
current_destination = None # Track where we're currently moving to
|
|
current_move = None # Track current move direction
|
|
|
|
# Store animation references
|
|
player_anim_x = None
|
|
player_anim_y = None
|
|
grid_anim_x = None
|
|
grid_anim_y = None
|
|
|
|
# Debug display caption
|
|
debug_caption = mcrfpy.Caption((10, 40),
|
|
text="Last: (4, 4) | Queue: 0 | Dest: None",
|
|
)
|
|
debug_caption.font_size = 16
|
|
debug_caption.fill_color = mcrfpy.Color(255, 255, 0, 255)
|
|
mcrfpy.sceneUI("tutorial").append(debug_caption)
|
|
|
|
# Additional debug caption for movement state
|
|
move_debug_caption = mcrfpy.Caption((10, 60),
|
|
text="Moving: False | Current: None | Queued: None",
|
|
)
|
|
move_debug_caption.font_size = 16
|
|
move_debug_caption.fill_color = mcrfpy.Color(255, 200, 0, 255)
|
|
mcrfpy.sceneUI("tutorial").append(move_debug_caption)
|
|
|
|
def key_to_direction(key):
|
|
"""Convert key to direction string"""
|
|
if key == "W" or key == "Up":
|
|
return "Up"
|
|
elif key == "S" or key == "Down":
|
|
return "Down"
|
|
elif key == "A" or key == "Left":
|
|
return "Left"
|
|
elif key == "D" or key == "Right":
|
|
return "Right"
|
|
return None
|
|
|
|
def update_debug_display():
|
|
"""Update the debug caption with current state"""
|
|
queue_count = len(move_queue)
|
|
dest_text = f"({current_destination[0]}, {current_destination[1]})" if current_destination else "None"
|
|
debug_caption.text = f"Last: ({player.x}, {player.y}) | Queue: {queue_count} | Dest: {dest_text}"
|
|
|
|
# Update movement state debug
|
|
current_dir = key_to_direction(current_move) if current_move else "None"
|
|
queued_dir = key_to_direction(move_queue[0]) if move_queue else "None"
|
|
move_debug_caption.text = f"Moving: {is_moving} | Current: {current_dir} | Queued: {queued_dir}"
|
|
|
|
# Animation completion callback
|
|
def movement_complete(anim, target):
|
|
"""Called when movement animation completes"""
|
|
global is_moving, move_queue, current_destination, current_move
|
|
global player_anim_x, player_anim_y
|
|
print(f"In callback for animation: {anim=} {target=}")
|
|
# Clear movement state
|
|
is_moving = False
|
|
current_move = None
|
|
current_destination = None
|
|
# Clear animation references
|
|
player_anim_x = None
|
|
player_anim_y = None
|
|
|
|
# Update last position to where we actually are now
|
|
#last_position = (int(player.x), int(player.y))
|
|
|
|
# Ensure grid is centered on final position
|
|
grid.center = (player.x + 0.5) * 16, (player.y + 0.5) * 16
|
|
|
|
# Check if there's a queued move
|
|
if move_queue:
|
|
# Pop the next move from the queue
|
|
next_move = move_queue.pop(0)
|
|
print(f"Processing queued move: {next_move}")
|
|
# Process it like a fresh input
|
|
process_move(next_move)
|
|
|
|
update_debug_display()
|
|
|
|
motion_speed = 0.30 # seconds per tile
|
|
|
|
def process_move(key):
|
|
"""Process a move based on the key"""
|
|
global is_moving, current_move, current_destination, move_queue
|
|
global player_anim_x, player_anim_y, grid_anim_x, grid_anim_y
|
|
|
|
# If already moving, just update the queue
|
|
if is_moving:
|
|
print(f"process_move processing {key=} as a queued move (is_moving = True)")
|
|
# Clear queue and add new move (only keep 1 queued move)
|
|
move_queue.clear()
|
|
move_queue.append(key)
|
|
update_debug_display()
|
|
return
|
|
print(f"process_move processing {key=} as a new, immediate animation (is_moving = False)")
|
|
# Calculate new position from current position
|
|
px, py = int(player.x), int(player.y)
|
|
new_x, new_y = px, py
|
|
|
|
# Calculate new position based on key press (only one tile movement)
|
|
if key == "W" or key == "Up":
|
|
new_y -= 1
|
|
elif key == "S" or key == "Down":
|
|
new_y += 1
|
|
elif key == "A" or key == "Left":
|
|
new_x -= 1
|
|
elif key == "D" or key == "Right":
|
|
new_x += 1
|
|
|
|
# Start the move if position changed
|
|
if new_x != px or new_y != py:
|
|
is_moving = True
|
|
current_move = key
|
|
current_destination = (new_x, new_y)
|
|
# only animate a single axis, same callback from either
|
|
if new_x != px:
|
|
player_anim_x = mcrfpy.Animation("x", float(new_x), motion_speed, "easeInOutQuad", callback=movement_complete)
|
|
player_anim_x.start(player)
|
|
elif new_y != py:
|
|
player_anim_y = mcrfpy.Animation("y", float(new_y), motion_speed, "easeInOutQuad", callback=movement_complete)
|
|
player_anim_y.start(player)
|
|
|
|
# Animate grid center to follow player
|
|
grid_anim_x = mcrfpy.Animation("center_x", (new_x + 0.5) * 16, motion_speed, "linear")
|
|
grid_anim_y = mcrfpy.Animation("center_y", (new_y + 0.5) * 16, motion_speed, "linear")
|
|
grid_anim_x.start(grid)
|
|
grid_anim_y.start(grid)
|
|
|
|
update_debug_display()
|
|
|
|
# Define keyboard handler
|
|
def handle_keys(key, state):
|
|
"""Handle keyboard input to move the player"""
|
|
if state == "start":
|
|
# Only process movement keys
|
|
if key in ["W", "Up", "S", "Down", "A", "Left", "D", "Right"]:
|
|
print(f"handle_keys producing actual input: {key=}")
|
|
process_move(key)
|
|
|
|
|
|
# Register the keyboard handler
|
|
mcrfpy.keypressScene(handle_keys)
|
|
|
|
# Add a title caption
|
|
title = mcrfpy.Caption((320, 10),
|
|
text="McRogueFace Tutorial - Part 2 Enhanced",
|
|
)
|
|
title.fill_color = mcrfpy.Color(255, 255, 255, 255)
|
|
mcrfpy.sceneUI("tutorial").append(title)
|
|
|
|
# Add instructions
|
|
instructions = mcrfpy.Caption((150, 750),
|
|
text="One-move queue system with animation callbacks!",
|
|
)
|
|
instructions.font_size=18
|
|
instructions.fill_color = mcrfpy.Color(200, 200, 200, 255)
|
|
mcrfpy.sceneUI("tutorial").append(instructions)
|
|
|
|
print("Tutorial Part 2 Enhanced loaded!")
|
|
print(f"Player entity created at grid position (4, 4)")
|
|
print("Movement now uses animation callbacks to prevent race conditions!")
|
|
print("Use WASD or Arrow keys to move!")
|