Files
prosperon/examples/snake/main.ce
2026-02-24 21:08:46 -06:00

193 lines
5.0 KiB
Plaintext

var core = use('core')
var camera = use('camera')
var compositor = use('compositor')
var input = use('input')
var shape = use('shape2d')
var text2d = use('text2d')
var tw = use('tween')
var ease = use('ease')
var random = use('random')
var GW = 600, GH = 600
var cellSize = 20
var gridW = GW / cellSize
var gridH = GH / cellSize
var game_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}})
var hud_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}})
// Action mapping with possession
input.configure({
action_map: {
up: ['w', 'up'],
down: ['s', 'down'],
left: ['a', 'left'],
right: ['d', 'right'],
restart: ['space']
}
})
// Game state
var snake_shapes = []
var snake_pos = []
var dir = {x: 1, y: 0}
var next_dir = {x: 1, y: 0}
var move_timer = 0
var move_interval = 0.12
var state = 'playing'
var gameover_label = null
var score = 0
var score_label = null
// Input handler entity — possessed by player1
var game_input = {
on_input: function(action, data) {
if (!data.pressed) return
if (state == 'playing') {
if (action == 'up' && dir.y != -1) next_dir = {x: 0, y: 1}
if (action == 'down' && dir.y != 1) next_dir = {x: 0, y: -1}
if (action == 'left' && dir.x != 1) next_dir = {x: -1, y: 0}
if (action == 'right' && dir.x != -1) next_dir = {x: 1, y: 0}
}
if (action == 'restart' && state == 'gameover') reset_game()
}
}
input.player1().possess(game_input)
function grid_to_world(gx, gy) {
return {x: gx * cellSize + cellSize / 2, y: gy * cellSize + cellSize / 2}
}
// Apple
var apple_gx = 0, apple_gy = 0
var apple_shape = shape.rect({
pos: {x: 0, y: 0}, width: cellSize - 2, height: cellSize - 2,
fill: {r: 1, g: 0.2, b: 0.2, a: 1}, plane: 'game'
})
function spawn_apple() {
apple_gx = floor(random.random() * gridW)
apple_gy = floor(random.random() * gridH)
var i = 0
for (i = 0; i < length(snake_pos); i++) {
if (snake_pos[i].x == apple_gx && snake_pos[i].y == apple_gy) {
spawn_apple()
return
}
}
var wp = grid_to_world(apple_gx, apple_gy)
apple_shape.pos.x = wp.x
apple_shape.pos.y = wp.y
}
function reset_game() {
var i = 0
for (i = 0; i < length(snake_shapes); i++)
snake_shapes[i].destroy()
snake_shapes = []
snake_pos = []
var cx = floor(gridW / 2)
var cy = floor(gridH / 2)
var wp = null
for (i = 0; i < 3; i++) {
push(snake_pos, {x: cx - i, y: cy})
wp = grid_to_world(cx - i, cy)
push(snake_shapes, shape.rect({
pos: {x: wp.x, y: wp.y}, width: cellSize - 2, height: cellSize - 2,
fill: {r: 0, g: 1, b: 0.3, a: 1}, plane: 'game'
}))
}
dir = {x: 1, y: 0}
next_dir = {x: 1, y: 0}
state = 'playing'
move_timer = 0
score = 0
if (score_label) score_label.text = "Score: 0"
if (gameover_label) { gameover_label.destroy(); gameover_label = null }
spawn_apple()
}
// HUD
score_label = text2d({
text: "Score: 0", pos: {x: 10, y: GH - 30},
plane: 'hud', size: 16, color: {r: 1, g: 1, b: 1, a: 1}
})
reset_game()
var comp_config = {
clear: {r: 0.08, g: 0.08, b: 0.1, a: 1},
planes: [
{name: 'game', camera: game_cam, resolution: {width: GW, height: GH}, presentation: 'letterbox'},
{name: 'hud', camera: hud_cam, resolution: {width: GW, height: GH}, presentation: 'stretch'}
]
}
core.start({
width: 600, height: 600, title: "Snake",
update: function(dt) {
if (state != 'playing') return
move_timer += dt
if (move_timer < move_interval) return
move_timer -= move_interval
dir.x = next_dir.x
dir.y = next_dir.y
// New head position
var hx = snake_pos[0].x + dir.x
var hy = snake_pos[0].y + dir.y
// Wrap
if (hx < 0) hx = gridW - 1
if (hx >= gridW) hx = 0
if (hy < 0) hy = gridH - 1
if (hy >= gridH) hy = 0
// Self collision
var i = 0
for (i = 0; i < length(snake_pos); i++) {
if (snake_pos[i].x == hx && snake_pos[i].y == hy) {
state = 'gameover'
gameover_label = text2d({
text: "GAME OVER — SPACE to restart",
pos: {x: GW / 2 - 170, y: GH / 2},
plane: 'hud', size: 20, color: {r: 1, g: 0.3, b: 0.3, a: 1}
})
return
}
}
// Add head with tween from old head position
var old_wp = grid_to_world(snake_pos[0].x, snake_pos[0].y)
var new_wp = grid_to_world(hx, hy)
snake_pos.unshift({x: hx, y: hy})
var head = shape.rect({
pos: {x: old_wp.x, y: old_wp.y}, width: cellSize - 2, height: cellSize - 2,
fill: {r: 0, g: 1, b: 0.3, a: 1}, plane: 'game'
})
// Smooth tween from old position to new position
tw.tween(head.pos).to({x: new_wp.x, y: new_wp.y}, move_interval).ease(ease.linear)
snake_shapes.unshift(head)
// Eat apple?
if (hx == apple_gx && hy == apple_gy) {
score++
score_label.text = "Score: " + score
spawn_apple()
} else {
// Remove tail
var tail = pop(snake_shapes)
tail.destroy()
pop(snake_pos)
}
},
render: function() {
return compositor.execute(compositor.compile(comp_config))
}
})