Skip to main content

grid-movement

Grid Movement in FlatRedBall2. Use when implementing tile-by-tile (grid-snapped) movement where one key press = one tile step, input is locked during the movement animation, and the player cannot move into blocked tiles. Trigger on any dungeon-crawler, Pokémon-style, roguelike, or puzzle game with discrete grid movement. Do NOT trigger for analog/free top-down movement — use the top-down-movement skill for that.

설치로 이동

소스 정보

저장소
vchelaru/FlatRedBall2
최근 소스 활동
2026년 7월 11일 18:17
감지된 SKILL.md 언어
영어
스타
14
포크
6

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
grid-movement
description
Grid Movement in FlatRedBall2. Use when implementing tile-by-tile (grid-snapped) movement where one key press = one tile step, input is locked during the movement animation, and the player cannot move into blocked tiles. Trigger on any dungeon-crawler, Pokémon-style, roguelike, or puzzle game with discrete grid movement. Do NOT trigger for analog/free top-down movement — use the top-down-movement skill for that.
# Grid Movement in FlatRedBall2 > **Not `TopDownBehavior`** — `TopDownBehavior` produces continuous analog velocity-based movement. For tile-by-tile grid movement, implement the pattern below directly. Do not load the `top-down-movement` skill for this use case. Grid movement has three invariants: 1. The entity always sits on a tile boundary (position snapped to multiples of `TileSize`). 2. Input is locked while a move is in progress. 3. A pre-move collision check prevents entering blocked tiles. ## Minimal Implementation ```csharp private const float TileSize = 16f; private bool _moving = false; public override void CustomInitialize() { X = MathF.Round(X / TileSize) * TileSize; // snap to grid on spawn Y = MathF.Round(Y / TileSize) * TileSize; } public override async void CustomActivity(FrameTime time) { if (_moving) return; // gate: one move at a time var (dx, dy) = ReadDirectionalInput(); if (dx == 0 && dy == 0) return; float targetX = X + dx * TileSize; float targetY = Y + dy * TileSize; if (IsBlocked(targetX, targetY)) return; // pre-move collision check _moving = true; await TweenTo(targetX, targetY, 0.12f); // animate the step X = targetX; Y = targetY; // snap to exact grid position _moving = false; } ``` ## Pre-Move Collision Check Use `TileShapes.GetTileAtWorld(x, y)` to test the target tile before committing: ```csharp private bool IsBlocked(float x, float y) => _solidTiles.GetTileAtWorld(x, y) != null; ``` `TileShapes` is injected from the screen after spawning via a method on the entity. See the `levels` skill for how to generate it from a TMX layer. > **Namespace gotcha** — `TileShapes` lives in `FlatRedBall2.Collision`, not `FlatRedBall2.Tiled`. Add `using FlatRedBall2.Collision;` to any file that declares a `TileShapes` field. ## Tween Helper A lerp tween using `Engine.Time.CurrentScreenTime` (accessible from any entity via `Engine`): ```csharp private async Task TweenTo(float tx, float ty, TimeSpan duration) { float startX = X, startY = Y; TimeSpan startTime = Engine.Time.CurrentScreenTime; await Engine.Time.DelayUntil(() => { TimeSpan elapsed = Engine.Time.CurrentScreenTime - startTime; float t = (float)Math.Clamp(elapsed.TotalSeconds / duration.TotalSeconds, 0.0, 1.0); X = startX + (tx - startX) * t; Y = startY + (ty - startY) * t; return t >= 1f; }, CancellationToken.None); } ``` > **`CancellationToken` scope** — `Token` is a property of `Screen`, not `Entity`. Pass `CancellationToken.None` from an entity. For tweens this short (<200 ms) that is safe; if you need cancellation on screen exit, thread a token in via a constructor parameter. ## Gotchas - **Always snap on spawn** — entities loaded from a TMX object layer may have sub-pixel offsets. Round `X` and `Y` to the nearest tile in `CustomInitialize`. - **`_moving` guard is required** — without it, holding a key queues multiple moves before the first frame completes. - **Diagonal movement**: If 8-way is needed, check that both axis tiles are clear before committing. Diagonal into a corner with one axis blocked should push to the open axis or reject. - **Entity origin and draw order**: if entities need screen-depth sorting (walking behind/in front of scenery), `(X,Y)` should be the ground-contact point and `Z = -Y` each frame — see `top-down-movement`'s Entity Origin and Draw Order section.
GitHub에서 보기