canvas API
This commit is contained in:
363
tests/canvas.html
Normal file
363
tests/canvas.html
Normal file
@@ -0,0 +1,363 @@
|
||||
<head>
|
||||
<title>Complete Canvas API Test - GURT Browser</title>
|
||||
<icon src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Lua-Logo.svg/256px-Lua-Logo.svg.png">
|
||||
<meta name="theme-color" content="#8b5cf6">
|
||||
<meta name="description" content="Comprehensive test of all Canvas 2D API functions">
|
||||
|
||||
<style>
|
||||
body { bg-[#f8fafc] p-6 }
|
||||
h1 { text-[#8b5cf6] text-3xl font-bold text-center }
|
||||
h2 { text-[#7c3aed] text-xl font-semibold mb-3 }
|
||||
h3 { text-[#6d28d9] text-lg font-medium mb-2 }
|
||||
.container { bg-[#ffffff] p-6 rounded-lg shadow-lg max-w-6xl mx-auto }
|
||||
.canvas-grid { display-grid grid-cols-2 gap-6 }
|
||||
.test-section { bg-[#f8fafc] p-4 rounded-lg border border-[#e2e8f0] mb-4 }
|
||||
.canvas-item { text-center p-4 }
|
||||
.description { text-[#64748b] text-sm mb-2 }
|
||||
canvas { border border-[#cbd5e1] rounded-lg bg-white }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
gurt.log('Starting comprehensive Canvas API test...')
|
||||
|
||||
-- Test 1: Basic Rectangle and Circle Drawing
|
||||
local basicCanvas = gurt.select("#basic-canvas")
|
||||
local basicCtx = basicCanvas:withContext("2d")
|
||||
|
||||
-- Background
|
||||
basicCtx:fillRect(0, 0, 400, 300, "#f1f5f9")
|
||||
|
||||
-- Basic rectangles
|
||||
basicCtx:fillRect(20, 20, 60, 40, "#ef4444")
|
||||
basicCtx:strokeRect(100, 20, 60, 40, "#22c55e", 3)
|
||||
basicCtx:clearRect(30, 30, 20, 20)
|
||||
|
||||
-- Circles
|
||||
basicCtx:drawCircle(200, 50, 25, "#3b82f6", true)
|
||||
basicCtx:drawCircle(280, 50, 25, "#f59e0b", false)
|
||||
|
||||
-- Text
|
||||
basicCtx:drawText(20, 100, "Basic Drawing Functions", "#1f2937")
|
||||
basicCtx:drawText(20, 120, "fillRect, strokeRect, clearRect, drawCircle, drawText", "#6b7280")
|
||||
|
||||
-- Test 2: Path-Based Drawing
|
||||
local pathCanvas = gurt.select("#path-canvas")
|
||||
local pathCtx = pathCanvas:withContext("2d")
|
||||
|
||||
pathCtx:fillRect(0, 0, 400, 300, "#f1f5f9")
|
||||
pathCtx:setStrokeStyle("#dc2626")
|
||||
pathCtx:setFillStyle("#fecaca")
|
||||
pathCtx:setLineWidth(3)
|
||||
|
||||
-- Simple test - draw two straight lines first
|
||||
pathCtx:setStrokeStyle("#ff0000")
|
||||
pathCtx:setLineWidth(5)
|
||||
pathCtx:beginPath()
|
||||
pathCtx:moveTo(50, 50)
|
||||
pathCtx:lineTo(150, 50)
|
||||
pathCtx:stroke()
|
||||
|
||||
-- Triangle
|
||||
pathCtx:beginPath()
|
||||
pathCtx:moveTo(50, 100)
|
||||
pathCtx:lineTo(100, 100)
|
||||
pathCtx:lineTo(75, 150)
|
||||
pathCtx:closePath()
|
||||
pathCtx:fill()
|
||||
pathCtx:stroke()
|
||||
|
||||
-- Complex path
|
||||
pathCtx:setStrokeStyle("#2563eb")
|
||||
pathCtx:beginPath()
|
||||
pathCtx:moveTo(150, 50)
|
||||
pathCtx:lineTo(200, 70)
|
||||
pathCtx:lineTo(180, 120)
|
||||
pathCtx:lineTo(150, 100)
|
||||
pathCtx:stroke()
|
||||
|
||||
-- Arc/Circle path
|
||||
pathCtx:setFillStyle("#10b981")
|
||||
pathCtx:beginPath()
|
||||
pathCtx:arc(300, 75, 30, 0, 2 * math.pi, false)
|
||||
pathCtx:fill()
|
||||
|
||||
-- Half circle
|
||||
pathCtx:setStrokeStyle("#f59e0b")
|
||||
pathCtx:setLineWidth(4)
|
||||
pathCtx:beginPath()
|
||||
pathCtx:arc(350, 75, 25, 0, math.pi, false)
|
||||
pathCtx:stroke()
|
||||
|
||||
pathCtx:drawText(20, 200, "Path-Based Drawing", "#1f2937")
|
||||
pathCtx:drawText(20, 220, "beginPath, moveTo, lineTo, closePath, arc, stroke, fill", "#6b7280")
|
||||
|
||||
-- Test 3: Transformations
|
||||
local transformCanvas = gurt.select("#transform-canvas")
|
||||
local transformCtx = transformCanvas:withContext("2d")
|
||||
|
||||
-- Background
|
||||
transformCtx:setFillStyle("#f1f5f9")
|
||||
transformCtx:fillRect(0, 0, 400, 300)
|
||||
|
||||
-- Original square (no transform)
|
||||
transformCtx:setFillStyle("#e2e8f0")
|
||||
transformCtx:fillRect(50, 50, 40, 40)
|
||||
|
||||
-- Simple translation test
|
||||
transformCtx:save()
|
||||
transformCtx:translate(100, 0)
|
||||
transformCtx:setFillStyle("#ef4444")
|
||||
transformCtx:fillRect(50, 50, 40, 40) -- Should appear at (150, 50)
|
||||
transformCtx:restore()
|
||||
|
||||
-- Simple rotation test
|
||||
transformCtx:save()
|
||||
transformCtx:translate(200, 70) -- Move origin to (200,70)
|
||||
transformCtx:rotate(math.pi / 4) -- Rotate 45 degrees
|
||||
transformCtx:setFillStyle("#22c55e")
|
||||
transformCtx:fillRect(-20, -20, 40, 40) -- Draw centered on new origin
|
||||
transformCtx:restore()
|
||||
|
||||
-- Simple scaling test
|
||||
transformCtx:save()
|
||||
transformCtx:translate(300, 70)
|
||||
transformCtx:scale(1.5, 0.8)
|
||||
transformCtx:setFillStyle("#3b82f6")
|
||||
transformCtx:fillRect(-20, -20, 40, 40)
|
||||
transformCtx:restore()
|
||||
|
||||
-- Combined test
|
||||
transformCtx:save()
|
||||
transformCtx:translate(150, 180)
|
||||
transformCtx:rotate(math.pi / 6)
|
||||
transformCtx:scale(1.2, 1.2)
|
||||
transformCtx:setFillStyle("#f59e0b")
|
||||
transformCtx:fillRect(-25, -25, 50, 50)
|
||||
transformCtx:restore()
|
||||
|
||||
transformCtx:drawText(20, 250, "Transformations", "#1f2937")
|
||||
transformCtx:drawText(20, 270, "translate, rotate, scale, save, restore", "#6b7280")
|
||||
|
||||
-- Test 4: Curves and Advanced Paths
|
||||
local curveCanvas = gurt.select("#curve-canvas")
|
||||
local curveCtx = curveCanvas:withContext("2d")
|
||||
|
||||
curveCtx:fillRect(0, 0, 400, 300, "#f1f5f9")
|
||||
curveCtx:setStrokeStyle("#8b5cf6")
|
||||
curveCtx:setLineWidth(3)
|
||||
|
||||
-- Quadratic curve
|
||||
curveCtx:beginPath()
|
||||
curveCtx:moveTo(50, 50)
|
||||
curveCtx:quadraticCurveTo(100, 20, 150, 50)
|
||||
curveCtx:stroke()
|
||||
|
||||
-- Bezier curve
|
||||
curveCtx:setStrokeStyle("#dc2626")
|
||||
curveCtx:beginPath()
|
||||
curveCtx:moveTo(200, 50)
|
||||
curveCtx:bezierCurveTo(220, 20, 280, 20, 300, 50)
|
||||
curveCtx:stroke()
|
||||
|
||||
-- Complex curved shape
|
||||
curveCtx:setStrokeStyle("#10b981")
|
||||
curveCtx:setFillStyle("#bbf7d0")
|
||||
curveCtx:beginPath()
|
||||
curveCtx:moveTo(100, 120)
|
||||
curveCtx:quadraticCurveTo(150, 90, 200, 120)
|
||||
curveCtx:quadraticCurveTo(230, 150, 200, 180)
|
||||
curveCtx:quadraticCurveTo(150, 210, 100, 180)
|
||||
curveCtx:quadraticCurveTo(70, 150, 100, 120)
|
||||
curveCtx:fill()
|
||||
curveCtx:stroke()
|
||||
|
||||
curveCtx:drawText(20, 250, "Curves and Advanced Paths", "#1f2937")
|
||||
curveCtx:drawText(20, 270, "quadraticCurveTo, bezierCurveTo", "#6b7280")
|
||||
|
||||
-- Test 5: Style Properties and Text
|
||||
local styleCanvas = gurt.select("#style-canvas")
|
||||
local styleCtx = styleCanvas:withContext("2d")
|
||||
|
||||
styleCtx:fillRect(0, 0, 400, 300, "#f1f5f9")
|
||||
|
||||
-- Different line widths
|
||||
for i = 1, 5 do
|
||||
styleCtx:setLineWidth(i * 2)
|
||||
styleCtx:setStrokeStyle("#" .. string.format("%02x", i * 40) .. "4040")
|
||||
styleCtx:beginPath()
|
||||
styleCtx:moveTo(20, 20 + i * 15)
|
||||
styleCtx:lineTo(120, 20 + i * 15)
|
||||
styleCtx:stroke()
|
||||
end
|
||||
|
||||
-- Different colors
|
||||
local colors = {"#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#8b5cf6", "#ec4899"}
|
||||
for i, color in ipairs(colors) do
|
||||
styleCtx:setFillStyle(color)
|
||||
styleCtx:fillRect(140 + (i-1) * 25, 30, 20, 60)
|
||||
end
|
||||
|
||||
-- Text with different properties
|
||||
styleCtx:setFont("20px sans-serif")
|
||||
styleCtx:drawText(20, 140, "Font Size Test", "#1f2937")
|
||||
|
||||
styleCtx:setFont("16px sans-serif")
|
||||
styleCtx:drawText(20, 170, "Medium Size Text", "#374151")
|
||||
|
||||
styleCtx:setFont("14px sans-serif")
|
||||
styleCtx:drawText(20, 190, "Small Size Text", "#4b5563")
|
||||
|
||||
-- Text measurement demo
|
||||
local testText = "Measured Text"
|
||||
local metrics = styleCtx:measureText(testText)
|
||||
styleCtx:drawText(200, 170, testText, "#dc2626")
|
||||
styleCtx:setStrokeStyle("#dc2626")
|
||||
styleCtx:strokeRect(200, 155, metrics.width, 20, "#dc2626", 1)
|
||||
|
||||
styleCtx:drawText(20, 250, "Styles and Text", "#1f2937")
|
||||
styleCtx:drawText(20, 270, "setStrokeStyle, setFillStyle, setLineWidth, setFont, measureText", "#6b7280")
|
||||
|
||||
-- Test 6: Complex Composition
|
||||
local complexCanvas = gurt.select("#complex-canvas")
|
||||
local complexCtx = complexCanvas:withContext("2d")
|
||||
|
||||
complexCtx:fillRect(0, 0, 400, 300, "#1e293b")
|
||||
|
||||
-- Create a complex scene with multiple techniques
|
||||
-- Background gradient effect (simulated with rectangles)
|
||||
for i = 0, 50 do
|
||||
local alpha = i / 50
|
||||
local gray = math.floor(30 + alpha * 40)
|
||||
local grayHex = string.format("#%02x%02x%02x", gray, gray, gray + 20)
|
||||
complexCtx:fillRect(0, i * 6, 400, 6, grayHex)
|
||||
end
|
||||
|
||||
-- Animated-looking shapes
|
||||
complexCtx:save()
|
||||
complexCtx:translate(200, 150)
|
||||
|
||||
for i = 1, 8 do
|
||||
complexCtx:save()
|
||||
complexCtx:rotate((i-1) * math.pi / 4)
|
||||
|
||||
-- Gradient-like effect with multiple rectangles
|
||||
for j = 1, 10 do
|
||||
local size = 60 - j * 5
|
||||
local alpha = j / 10
|
||||
local red = math.floor(139 + alpha * 100)
|
||||
local green = math.floor(69 + alpha * 150)
|
||||
local blue = math.floor(19 + alpha * 200)
|
||||
local colorHex = string.format("#%02x%02x%02x", red, green, blue)
|
||||
complexCtx:fillRect(-size/2, 80 - size/2, size, size, colorHex)
|
||||
end
|
||||
|
||||
complexCtx:restore()
|
||||
end
|
||||
|
||||
complexCtx:restore()
|
||||
|
||||
-- Central circle with path
|
||||
complexCtx:setFillStyle("#fbbf24")
|
||||
complexCtx:beginPath()
|
||||
complexCtx:arc(200, 150, 25, 0, 2 * math.pi)
|
||||
complexCtx:fill()
|
||||
|
||||
-- Overlay text
|
||||
complexCtx:setFont("24px sans-serif")
|
||||
complexCtx:drawText(150, 280, "Complex Scene", "#ffffff")
|
||||
|
||||
-- Shader Canvas Test
|
||||
local shaderCanvas = gurt.select("#shader-canvas")
|
||||
local shaderCtx = shaderCanvas:withContext("shader")
|
||||
|
||||
shaderCtx:source([[
|
||||
shader_type canvas_item;
|
||||
|
||||
uniform float time : hint_range(0.0, 10.0) = 1.0;
|
||||
|
||||
void fragment() {
|
||||
vec2 uv = UV;
|
||||
vec3 color = vec3(0.5 + 0.5 * cos(time + uv.xyx + vec3(0, 2, 4)));
|
||||
COLOR = vec4(color, 1.0);
|
||||
}
|
||||
]])
|
||||
|
||||
gurt.log('Canvas API test completed successfully!')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>🎨 Complete Canvas API Test Suite</h1>
|
||||
|
||||
<div style="container">
|
||||
<div style="test-section">
|
||||
<h2>📋 API Coverage Test</h2>
|
||||
<p style="description">This page tests every single Canvas 2D API function implemented in GURT:</p>
|
||||
<ul style="text-[#4b5563] text-sm space-y-1 mb-4">
|
||||
<li><strong>Basic Drawing:</strong> fillRect, strokeRect, clearRect, drawCircle, drawText</li>
|
||||
<li><strong>Path Drawing:</strong> beginPath, closePath, moveTo, lineTo, arc, stroke, fill</li>
|
||||
<li><strong>Transformations:</strong> save, restore, translate, rotate, scale</li>
|
||||
<li><strong>Advanced Paths:</strong> quadraticCurveTo, bezierCurveTo</li>
|
||||
<li><strong>Styling:</strong> setStrokeStyle, setFillStyle, setLineWidth, setFont</li>
|
||||
<li><strong>Text Measurement:</strong> measureText</li>
|
||||
<li><strong>Shader Support:</strong> withContext("shader"), source</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div style="canvas-grid">
|
||||
<!-- Row 1 -->
|
||||
<div style="canvas-item">
|
||||
<h3>Basic Drawing Functions</h3>
|
||||
<p style="description">Rectangle and circle primitives</p>
|
||||
<canvas id="basic-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div style="canvas-item">
|
||||
<h3>Path-Based Drawing</h3>
|
||||
<p style="description">Paths, lines, arcs, and shapes</p>
|
||||
<canvas id="path-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<div style="canvas-item">
|
||||
<h3>Transformations</h3>
|
||||
<p style="description">Translate, rotate, scale, save/restore</p>
|
||||
<canvas id="transform-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div style="canvas-item">
|
||||
<h3>Curves & Advanced Paths</h3>
|
||||
<p style="description">Quadratic and Bezier curves</p>
|
||||
<canvas id="curve-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<div style="canvas-item">
|
||||
<h3>Styles & Text</h3>
|
||||
<p style="description">Colors, line widths, fonts, text measurement</p>
|
||||
<canvas id="style-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div style="canvas-item">
|
||||
<h3>Complex Composition</h3>
|
||||
<p style="description">Multiple techniques combined</p>
|
||||
<canvas id="complex-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shader Test -->
|
||||
<div style="test-section">
|
||||
<h3>Shader Canvas Support</h3>
|
||||
<p style="description">Custom fragment shader with animated colors</p>
|
||||
<div style="text-center">
|
||||
<canvas id="shader-canvas" width="400" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="bg-[#f0f9ff] border border-[#0ea5e9] p-4 rounded-lg mt-6">
|
||||
<h3 style="text-[#0c4a6e] font-semibold mb-2">✅ Test Results</h3>
|
||||
<p style="text-[#0c4a6e] text-sm">All canvas functions should render correctly above. Check the browser console for any errors.</p>
|
||||
<p style="text-[#0c4a6e] text-sm mt-2">This test demonstrates compatibility with HTML5 Canvas API standards.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
237
tests/snake.html
Normal file
237
tests/snake.html
Normal file
@@ -0,0 +1,237 @@
|
||||
<head>
|
||||
<title>Snake Game - Canvas Demo</title>
|
||||
<icon src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/cf/Lua-Logo.svg/256px-Lua-Logo.svg.png">
|
||||
<meta name="theme-color" content="#22c55e">
|
||||
<meta name="description" content="Classic Snake game built with GURT Lua API and Canvas">
|
||||
|
||||
<style>
|
||||
body { bg-[#1f2937] p-6 }
|
||||
h1 { text-[#22c55e] text-3xl font-bold text-center }
|
||||
.container { bg-[#374151] p-6 rounded-lg shadow-lg max-w-2xl mx-auto }
|
||||
.game-info { bg-[#4b5563] p-4 rounded-lg mb-4 text-white }
|
||||
.score { text-[#fbbf24] text-2xl font-bold }
|
||||
.controls { bg-[#6b7280] p-4 rounded-lg text-[#f3f4f6] }
|
||||
.game-over { bg-[#ef4444] text-white p-4 rounded-lg text-center font-bold }
|
||||
.button { bg-[#22c55e] text-white px-4 py-2 rounded hover:bg-[#16a34a] cursor-pointer }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
-- Game constants
|
||||
local CANVAS_WIDTH = 400
|
||||
local CANVAS_HEIGHT = 400
|
||||
local GRID_SIZE = 20
|
||||
local GRID_WIDTH = CANVAS_WIDTH / GRID_SIZE
|
||||
local GRID_HEIGHT = CANVAS_HEIGHT / GRID_SIZE
|
||||
|
||||
-- Game state
|
||||
local snake = {{x = 10, y = 10}}
|
||||
local direction = {x = 1, y = 0}
|
||||
local food = {x = 15, y = 15}
|
||||
local score = 0
|
||||
local gameRunning = false
|
||||
local gameInterval = nil
|
||||
|
||||
-- Get DOM elements
|
||||
local canvas = gurt.select('#game-canvas')
|
||||
local ctx = canvas:withContext('2d')
|
||||
local scoreDisplay = gurt.select('#score')
|
||||
local gameOverDisplay = gurt.select('#game-over')
|
||||
local startButton = gurt.select('#start-button')
|
||||
|
||||
gameOverDisplay:hide()
|
||||
|
||||
-- Check if position is occupied by snake
|
||||
local function isSnakePosition(x, y)
|
||||
for i = 1, #snake do
|
||||
if snake[i].x == x and snake[i].y == y then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Generate random food position
|
||||
local function generateFood()
|
||||
repeat
|
||||
food.x = math.random(0, GRID_WIDTH - 1)
|
||||
food.y = math.random(0, GRID_HEIGHT - 1)
|
||||
until not isSnakePosition(food.x, food.y)
|
||||
end
|
||||
|
||||
-- Initialize game
|
||||
local function initGame()
|
||||
snake = {{x = 10, y = 10}}
|
||||
direction = {x = 1, y = 0}
|
||||
food = {x = 15, y = 15}
|
||||
score = 0
|
||||
gameRunning = true
|
||||
gameOverDisplay.text = ''
|
||||
gameOverDisplay:hide()
|
||||
scoreDisplay.text = 'Score: 0'
|
||||
generateFood()
|
||||
end
|
||||
|
||||
local function gameOver()
|
||||
gameRunning = false
|
||||
clearInterval(gameInterval)
|
||||
gameOverDisplay.text = 'Game Over! Final Score: ' .. score
|
||||
gameOverDisplay:show()
|
||||
end
|
||||
|
||||
local function moveSnake()
|
||||
if not gameRunning then return end
|
||||
|
||||
-- Calculate new head position
|
||||
local head = snake[1]
|
||||
local newHead = {
|
||||
x = head.x + direction.x,
|
||||
y = head.y + direction.y
|
||||
}
|
||||
|
||||
-- Check wall collision
|
||||
if newHead.x < 0 or newHead.x >= GRID_WIDTH or
|
||||
newHead.y < 0 or newHead.y >= GRID_HEIGHT then
|
||||
gameOver()
|
||||
return
|
||||
end
|
||||
|
||||
-- Check self collision
|
||||
if isSnakePosition(newHead.x, newHead.y) then
|
||||
gameOver()
|
||||
return
|
||||
end
|
||||
|
||||
-- Add new head
|
||||
table.insert(snake, 1, newHead)
|
||||
|
||||
-- Check food collision
|
||||
if newHead.x == food.x and newHead.y == food.y then
|
||||
score = score + 10
|
||||
scoreDisplay.text = 'Score: ' .. score
|
||||
generateFood()
|
||||
else
|
||||
-- Remove tail if no food eaten
|
||||
table.remove(snake, #snake)
|
||||
end
|
||||
end
|
||||
|
||||
-- Render game
|
||||
local function render()
|
||||
-- Clear canvas
|
||||
ctx:fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, '#1f2937')
|
||||
|
||||
-- Draw grid lines
|
||||
for i = 0, GRID_WIDTH do
|
||||
local x = i * GRID_SIZE
|
||||
ctx:strokeRect(x, 0, 1, CANVAS_HEIGHT, '#374151', 1)
|
||||
end
|
||||
for i = 0, GRID_HEIGHT do
|
||||
local y = i * GRID_SIZE
|
||||
ctx:strokeRect(0, y, CANVAS_WIDTH, 1, '#374151', 1)
|
||||
end
|
||||
|
||||
-- Draw snake
|
||||
for i = 1, #snake do
|
||||
local segment = snake[i]
|
||||
local x = segment.x * GRID_SIZE
|
||||
local y = segment.y * GRID_SIZE
|
||||
|
||||
-- Head is brighter green
|
||||
local color = i == 1 and '#22c55e' or '#16a34a'
|
||||
ctx:fillRect(x, y, GRID_SIZE - 2, GRID_SIZE - 2, color)
|
||||
end
|
||||
|
||||
-- Draw food
|
||||
local foodX = food.x * GRID_SIZE
|
||||
local foodY = food.y * GRID_SIZE
|
||||
ctx:fillRect(foodX, foodY, GRID_SIZE - 2, GRID_SIZE - 2, '#ef4444')
|
||||
end
|
||||
|
||||
-- Game loop
|
||||
local function gameLoop()
|
||||
if gameRunning then
|
||||
moveSnake()
|
||||
render()
|
||||
end
|
||||
end
|
||||
|
||||
-- Start game
|
||||
local function startGame()
|
||||
if gameInterval then
|
||||
clearInterval(gameInterval)
|
||||
end
|
||||
|
||||
initGame()
|
||||
render()
|
||||
gameInterval = setInterval(gameLoop, 150)
|
||||
end
|
||||
|
||||
-- Handle keyboard input
|
||||
gurt.body:on('keypress', function(event)
|
||||
if not gameRunning then return end
|
||||
|
||||
local key = event.keycode
|
||||
|
||||
-- Arrow keys or WASD
|
||||
if key == 37 or key == 65 then -- Left / A
|
||||
if direction.x ~= 1 then
|
||||
direction = {x = -1, y = 0}
|
||||
end
|
||||
elseif key == 38 or key == 87 then -- Up / W
|
||||
if direction.y ~= 1 then
|
||||
direction = {x = 0, y = -1}
|
||||
end
|
||||
elseif key == 39 or key == 68 then -- Right / D
|
||||
if direction.x ~= -1 then
|
||||
direction = {x = 1, y = 0}
|
||||
end
|
||||
elseif key == 40 or key == 83 then -- Down / S
|
||||
if direction.y ~= -1 then
|
||||
direction = {x = 0, y = 1}
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Start button handler
|
||||
startButton:on('click', function()
|
||||
startGame()
|
||||
end)
|
||||
|
||||
-- Initialize random seed and render initial state
|
||||
math.randomseed(Time.now())
|
||||
render()
|
||||
|
||||
gurt.log('Snake game initialized!')
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>🐍 Snake Game</h1>
|
||||
|
||||
<div style="container">
|
||||
<div style="game-info">
|
||||
<div style="w-full flex justify-between items-center">
|
||||
<div id="score" style="score">Score: 0</div>
|
||||
<button id="start-button" style="button">Start Game</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-center mb-4">
|
||||
<canvas id="game-canvas" width="400" height="400" style="border border-[#6b7280] rounded-lg bg-[#1f2937]"></canvas>
|
||||
</div>
|
||||
|
||||
<div id="game-over" style="game-over mb-4">Test</div>
|
||||
|
||||
<div style="controls">
|
||||
<h3 style="text-[#f9fafb] font-semibold mb-2">Controls:</h3>
|
||||
<p style="text-sm">Use <strong>Arrow Keys</strong> or <strong>WASD</strong> to control the snake</p>
|
||||
<ul style="text-sm mt-2 space-y-1">
|
||||
<li>🔼 Up: Arrow Up or W</li>
|
||||
<li>🔽 Down: Arrow Down or S</li>
|
||||
<li>◀️ Left: Arrow Left or A</li>
|
||||
<li>▶️ Right: Arrow Right or D</li>
|
||||
</ul>
|
||||
<p style="text-xs text-[#d1d5db] mt-3">Eat the red food to grow and increase your score!</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
Reference in New Issue
Block a user