refactor: reduce kart rotation frames from 16 to 8 and add racer artwork
- Add new racer spritesheets (8 angles × 64×64) for all 9 racers - Introduce KART_ROT_FRAMES constant as single source of truth for rotation frame count - Update SuperKartMode7.angleFrame(), SuperKartGame frame math, and procedural fallback to derive from KART_ROT_FRAMES instead of magic numbers - Update data/superkart-artwork.json with asset paths and 8-angle metadata - Update sprites.md documentation to reflect 8-view-angle contract
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1004 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
|
@ -5,13 +5,14 @@
|
|||
"supplied - set the path once the PNG exists and the game uses it with no",
|
||||
"code changes. Loaded lazily on game entry via src/data/assetManifest.js.",
|
||||
"",
|
||||
"racerSheets - one spritesheet per racer, 18 frames of 64x64 in a single row",
|
||||
" (1152x64 png). Frames 0..15 are the kart seen from 16 view angles:",
|
||||
"racerSheets - one spritesheet per racer, 10 frames of 64x64 in a single row",
|
||||
" (640x64 png). Frames 0..7 are the kart seen from 8 view angles:",
|
||||
" frame 0 = viewed square from BEHIND, rotating CLOCKWISE (seen from above)",
|
||||
" in 22.5-degree steps - frame 4 = kart's left side, frame 8 = head-on,",
|
||||
" frame 12 = right side. Frames 16/17 are optional player-view lean frames",
|
||||
" (16 = leaning left, 17 = leaning right); if the sheet is only 16 frames",
|
||||
" wide the game reuses frame 0 with a tilt instead.",
|
||||
" in 45-degree steps - frame 2 = kart's left side, frame 4 = head-on,",
|
||||
" frame 6 = right side (1/3/5/7 are the diagonal in-between views). Frames",
|
||||
" 8/9 are optional player-view lean frames (8 = leaning left, 9 = leaning",
|
||||
" right); if the sheet is only 8 frames wide the game reuses frame 0 with",
|
||||
" a tilt instead.",
|
||||
" Transparent background, kart centered, wheels touching the frame bottom.",
|
||||
"",
|
||||
"themeSheets - one 8-frame sheet per location theme, 64x64 frames in a row",
|
||||
|
|
@ -29,15 +30,15 @@
|
|||
" overdrive, emp, coins), then frame 7 = item box, frame 8 = coin."
|
||||
],
|
||||
"racerSheets": {
|
||||
"mario": { "key": "superkart-kart-mario", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"kona": { "key": "superkart-kart-kona", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"fireball": { "key": "superkart-kart-fireball", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"smasher": { "key": "superkart-kart-smasher", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"croc": { "key": "superkart-kart-croc", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"gerome": { "key": "superkart-kart-gerome", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"blackwind": { "key": "superkart-kart-blackwind", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"dv-8-2303": { "key": "superkart-kart-dv-8-2303", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 },
|
||||
"zanthor": { "key": "superkart-kart-zanthor", "path": null, "frameWidth": 64, "frameHeight": 64, "angles": 16 }
|
||||
"mario": { "key": "superkart-kart-mario", "path": "assets/images/superkart/racer-mario.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"kona": { "key": "superkart-kart-kona", "path": "assets/images/superkart/racer-kona.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"fireball": { "key": "superkart-kart-fireball", "path": "assets/images/superkart/racer-fireball.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"smasher": { "key": "superkart-kart-smasher", "path": "assets/images/superkart/racer-smasher.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"croc": { "key": "superkart-kart-croc", "path": "assets/images/superkart/racer-croc.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"gerome": { "key": "superkart-kart-gerome", "path": "assets/images/superkart/racer-gerome.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"blackwind": { "key": "superkart-kart-blackwind", "path": "assets/images/superkart/racer-blackwind.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"dv-8-2303": { "key": "superkart-kart-dv-8-2303", "path": "assets/images/superkart/racer-dv-8-2303.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 },
|
||||
"zanthor": { "key": "superkart-kart-zanthor", "path": "assets/images/superkart/racer-zanthor.png", "frameWidth": 64, "frameHeight": 64, "angles": 8 }
|
||||
},
|
||||
"themeSheets": {
|
||||
"beach": { "key": "superkart-theme-beach", "path": null, "frameWidth": 64, "frameHeight": 64 },
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
rasterizeTrack, drawMinimapCanvas, buildKartSheetCanvas, buildItemSheetCanvas,
|
||||
buildBackdropCanvas, buildDecorSheetCanvas,
|
||||
} from './SuperKartRaster.js';
|
||||
import { SuperKartMode7, Mode7Backdrop, MODE7 } from './SuperKartMode7.js';
|
||||
import { SuperKartMode7, Mode7Backdrop, MODE7, KART_ROT_FRAMES } from './SuperKartMode7.js';
|
||||
|
||||
const FONT = 'm6x11, "Julius Sans One"';
|
||||
const TIMES_KEY = 'superkart-times';
|
||||
|
|
@ -646,7 +646,7 @@ export default class SuperKartGame extends Phaser.Scene {
|
|||
let frame = null;
|
||||
if (tag === 'kart') {
|
||||
wx = ref.x; wy = ref.y;
|
||||
frame = this.mode7.angleFrame(ref.spinMs > 0 ? ref.heading + ref.spinSpin : ref.heading);
|
||||
frame = this.mode7.angleFrame(ref.spinMs > 0 ? ref.heading + ref.spinSpin : ref.heading, KART_ROT_FRAMES);
|
||||
visible = ref.rescueMs <= 0;
|
||||
} else if (tag === 'box') {
|
||||
visible = ref.respawnAt <= state.timeMs;
|
||||
|
|
@ -706,17 +706,17 @@ export default class SuperKartGame extends Phaser.Scene {
|
|||
renderPlayerKart(kart) {
|
||||
const s = this.playerSprite;
|
||||
const sheet = this.textures.get(this.kartTexKey(kart.racer.id));
|
||||
const hasLean = sheet.frameTotal > 17;
|
||||
const hasLean = sheet.frameTotal > KART_ROT_FRAMES + 1;
|
||||
const inputs = this.readInputs();
|
||||
let frame = 0;
|
||||
let angle = 0;
|
||||
if (kart.spinMs > 0) {
|
||||
frame = Math.floor((this.raceState.timeMs / 60)) % 16;
|
||||
frame = Math.floor((this.raceState.timeMs / 60)) % KART_ROT_FRAMES;
|
||||
} else if (kart.drifting) {
|
||||
frame = kart.driftDir > 0 ? 1 : 15;
|
||||
frame = kart.driftDir > 0 ? 1 : KART_ROT_FRAMES - 1;
|
||||
angle = kart.driftDir * 6;
|
||||
} else if (inputs.steer !== 0 && hasLean) {
|
||||
frame = inputs.steer < 0 ? 16 : 17;
|
||||
frame = inputs.steer < 0 ? KART_ROT_FRAMES : KART_ROT_FRAMES + 1;
|
||||
} else if (inputs.steer !== 0) {
|
||||
angle = inputs.steer * 5;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@ export const MODE7 = {
|
|||
spriteBase: 34, // world size (units) a 1.0-scale 64px sprite frame represents
|
||||
};
|
||||
|
||||
// Racer spritesheet rotation-frame count — the single source of truth for
|
||||
// the 8-view-angle kart art contract (data/superkart-artwork.json, the
|
||||
// procedural fallback in SuperKartRaster.js, and the frame math below all
|
||||
// derive from this instead of a repeated magic number).
|
||||
export const KART_ROT_FRAMES = 8;
|
||||
|
||||
const FRAG = `
|
||||
precision mediump float;
|
||||
|
||||
|
|
@ -153,8 +159,8 @@ export class SuperKartMode7 {
|
|||
};
|
||||
}
|
||||
|
||||
// View-angle frame for a kart heading (16-frame sheets, frame 0 = rear view).
|
||||
angleFrame(heading, angles = 16) {
|
||||
// View-angle frame for a kart heading (8-frame sheets, frame 0 = rear view).
|
||||
angleFrame(heading, angles = KART_ROT_FRAMES) {
|
||||
let rel = (heading - this.cam.angle) % TAU;
|
||||
if (rel < 0) rel += TAU;
|
||||
return Math.round((rel / TAU) * angles) % angles;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
// data/superkart-artwork.json are still path:null.
|
||||
|
||||
import { sampleAt, STRIPE_LEN, CURB_WIDTH } from './SuperKartTrack.js';
|
||||
import { KART_ROT_FRAMES } from './SuperKartMode7.js';
|
||||
|
||||
export const TRACK_TEXTURE_SIZE = 2048;
|
||||
|
||||
|
|
@ -275,18 +276,18 @@ export function drawMinimapCanvas(model, size = 220) {
|
|||
|
||||
// ── Procedural fallback sprites ─────────────────────────────────────────────
|
||||
|
||||
// 18-frame kart rotation strip (16 view angles + 2 lean frames) drawn as a
|
||||
// 10-frame kart rotation strip (8 view angles + 2 lean frames) drawn as a
|
||||
// simple tinted kart. Frame 0 = seen from behind, rotating clockwise — the
|
||||
// same contract as drop-in racer sheets in data/superkart-artwork.json.
|
||||
export function buildKartSheetCanvas(color, frame = 64) {
|
||||
const angles = 16;
|
||||
const angles = KART_ROT_FRAMES;
|
||||
const canvas = makeCanvas(frame * (angles + 2), frame);
|
||||
const ctx = canvas.getContext('2d');
|
||||
for (let i = 0; i < angles; i += 1) {
|
||||
drawKart(ctx, i * frame + frame / 2, frame / 2, (i * Math.PI * 2) / angles, color, frame, 0);
|
||||
}
|
||||
drawKart(ctx, 16 * frame + frame / 2, frame / 2, 0, color, frame, -0.35);
|
||||
drawKart(ctx, 17 * frame + frame / 2, frame / 2, 0, color, frame, 0.35);
|
||||
drawKart(ctx, angles * frame + frame / 2, frame / 2, 0, color, frame, -0.35);
|
||||
drawKart(ctx, (angles + 1) * frame + frame / 2, frame / 2, 0, color, frame, 0.35);
|
||||
return canvas;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,17 +26,21 @@ All racer/theme/item art needs a **transparent background**.
|
|||
| `zanthor` | Zanthor |
|
||||
|
||||
- **Frame size:** 64×64
|
||||
- **Layout:** 18 frames in a single row → **1152×64 px**
|
||||
- **Layout:** 10 frames in a single row → **640×64 px**
|
||||
- **File:** set `racerSheets.<id>.path` in `data/superkart-artwork.json`
|
||||
- **Frames 0–15** — the kart viewed from 16 angles, rotating **clockwise as
|
||||
seen from above**, in 22.5° steps:
|
||||
- **Frames 0–7** — the kart viewed from 8 angles, rotating **clockwise as
|
||||
seen from above**, in 45° steps:
|
||||
- frame 0 = dead-on from **behind**
|
||||
- frame 4 = kart's left side
|
||||
- frame 8 = head-on (facing the viewer)
|
||||
- frame 12 = kart's right side
|
||||
- **Frames 16–17** — optional player-view lean frames (16 = leaning left,
|
||||
17 = leaning right), used only for the player's fixed on-screen kart while
|
||||
steering. If the sheet is only 16 frames wide, the game reuses frame 0 with
|
||||
- frame 1 = diagonal, between behind and left
|
||||
- frame 2 = kart's left side
|
||||
- frame 3 = diagonal, between left and head-on
|
||||
- frame 4 = head-on (facing the viewer)
|
||||
- frame 5 = diagonal, between head-on and right
|
||||
- frame 6 = kart's right side
|
||||
- frame 7 = diagonal, between right and behind
|
||||
- **Frames 8–9** — optional player-view lean frames (8 = leaning left,
|
||||
9 = leaning right), used only for the player's fixed on-screen kart while
|
||||
steering. If the sheet is only 8 frames wide, the game reuses frame 0 with
|
||||
a programmatic tilt instead — fine to skip.
|
||||
- Kart centered horizontally in each frame, wheels touching the bottom edge
|
||||
(world sprites are anchored a bit above the frame's bottom, not dead
|
||||
|
|
|
|||