无需Canvas的平台跳跃游戏实现及优化建议咨询
无Canvas平台跳跃游戏:项目展示与优化建议
嘿,这个不依赖Canvas的平台跳跃游戏想法挺有意思的!先给你把项目代码按模块结构化展示,再从代码结构、性能、游戏体验几个方面聊聊具体的优化建议:
项目代码展示
CSS 样式
#character { position: absolute; background: blue; top: 0px; left: 0px; width: 50px; height: 50px; z-index: 1; } #box1 { position: absolute; background-color: #D0D0D0; top: 250px; left: 0px; width: 500px; height: 50px; z-index: 0; } #box2 { position: absolute; background-color: #D0D0D0; top: 200px; left: 400px; width: 50px; height: 50px; z-index: 0; } #box3 { position: absolute; background-color: #D0D0D0; top: 500px; left: 600px; width: 250px; height: 50px; z-index: 0; } #box4 { position: absolute; background-color: #D0D0D0; top: 500px; left: 400px; width: 50px; height: 50px; z-index: 0; } #box5 { position: absolute; background-color: #D0D0D0; top: 300px; left: 700px; width: 300px; height: 50px; z-index: 0; } #box6 { position: absolute; background-color: #D0D0D0; top: 200px; left: 1050px; width: 200px; height: 50px; z-index: 0; } #box7 { position: absolute; background-color: #D0D0D0; top: 550px; left: 450px; width: 50px; height: 50px; } #trap1 { position: absolute; background-color: red; top: 300px; left: 500px; width: 100px; height: 50px; z-index: 0; } #trap2 { position: absolute; background-color: red; top: 250px; left: 1000px; width: 50px; height: 50px; z-index: 0; } #end { position: absolute; background-color: yellow; top: 100px; left: 1225px; width: 25px; height: 25px; z-index: 0; }
HTML 结构
<body id=my> <a>use W A D to move</a> <div id="character"></div> <div id="box1" class="box"></div> <div id="box2" class="box"></div> <div id="box3" class="box"></div> <div id="box4" class="box"></div> <div id="box5" class="box"></div> <div id="box6" class="box"></div> <div id="box7" class="box"></div> <div id="trap1" class="box"></div> <div id="trap2" class="box"></div> <div id="end" class="box"></div> </body>
JavaScript 逻辑
var currentKey; //records the current key pressed var charSpeed = 40; //how fast the character will move var coll = setInterval(col, 5); function checkb(cb) { return cb == "b"; } function checkr(cr) { return cr == "r"; } function checkl(cl) { return cl == "l"; } function checkt(ct) { return ct == "t"; } function checke(ce) { return ce == "e"; } function col() { var c = [colCheck($('#character'), $('#box1')), colCheck($('#character'), $('#box2')), colCheck($('#character'), $('#box3')), colCheck($('#character'), $('#box4')), colCheck($('#character'), $('#box5')), colCheck($('#character'), $('#box6')), colCheck($('#character'), $('#box7'))]; var col = collision($('#character'), $('#trap1')); var col1 = collision($('#character'), $('#trap2')); var col2 = collision($('#character'), $('#end')); if (col == true || col1 == true) { self.location['replace'](location); clearInterval(coll); } if (col2 == true) { alert("You Won!!"); self.location['replace'](location); clearInterval(coll); } if (c.some(checkr)) { $('#character').css("left", '-=15'); } if (c.some(checkl)) { $('#character').css("left", '+=15'); } if (c.some(checkb)) {} if (c.some(checkt)) { $('#character').css("top", '+=45'); } if (!c.some(checkb)) { $('#character').css("top", '+=2'); } } document.addEventListener('keydown', function(ev) { return onkey(ev, ev.keyCode, true); }, false); document.addEventListener('keyup', function(ev) { return onkey(ev, ev.keyCode, false); }, false); function onkey(ev, key, down) { if (down && $('#character').queue("fx").length == 0) { if (key == 100 || key == 68 ) { moveChar("right"); } else if (key == 65 || key == 97 ) { moveChar("left"); } else if (key == 87 || key == 119 ) { moveCharu("up"); } } } function collision($div1, $div2) { var x1 = $div1.offset().left; var y1 = $div1.offset().top; var h1 = $div1.outerHeight(true); var w1 = $div1.outerWidth(true); var b1 = y1 + h1; var r1 = x1 + w1; var x2 = $div2.offset().left; var y2 = $div2.offset().top; var h2 = $div2.outerHeight(true); var w2 = $div2.outerWidth(true); var b2 = y2 + h2; var r2 = x2 + w2; if (b1 < y2 || y1 > b2 || r1 < x2 || x1 > r2) return false; return true; } function colCheck($div1, $div2) { // get the vectors to check against var vX = ($div1.offset().left + ($div1.outerWidth(true) / 2)) - ($div2.offset().left + ($div2.outerWidth(true) / 2)), vY = ($div1.offset().top + ($div1.outerHeight(true) / 2)) - ($div2.offset().top + ($div2.outerHeight(true) / 2)), // add the half widths and half heights of the objects hWidths = ($div1.outerWidth(true) / 2) + ($div2.outerWidth(true) / 2), hHeights = ($div1.outerHeight(true) / 2) + ($div2.outerHeight(true) / 2), colDir = "e"; // if the x and y vector are less than the half width or half height, they we must be inside the object, causing a collision if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) { // figures out on which side we are colliding (top, bottom, left, or right) var oX = hWidths - Math.abs(vX), oY = hHeights - Math.abs(vY); if (oX >= oY) { if (vY > 0) { colDir = "t"; $div1.offset().top += oY; } else { colDir = "b"; $div1.offset().top -= oY; } } else { if (vX > 0) { colDir = "l"; $div1.offset().left += oX; } else { colDir = "r"; $div1.offset().left -= oX; } } } return colDir; } //Process Character Move Function function moveChar(dir) { if (dir == "left") { //don't let the character move any further left if they are already at the left side of the screen if ($('#character').position().left > 0) { $('#character').animate({ left: '-=15' }, charSpeed); } } if (dir == "right") { $('#character').animate({ left: '+=15' }, charSpeed); } } function moveCharu(dir, ) { var c = [colCheck($('#character'), $('#box1')), colCheck($('#character'), $('#box2')), colCheck($('#character'), $('#box3')), colCheck($('#character'), $('#box4')), colCheck($('#character'), $('#box5')), colCheck($('#character'), $('#box6')), colCheck($('#character'), $('#box7'))]; if (dir == "up") { //don't let the character move any further up if they are already at the top of the screen and also don't let char air jump. if (c.some(checkb)) { var jump = setInterval(function() { $('#character').css({ top: '-=8' }, ); }, 1); var clearjump = setTimeout(function() { clearInterval(jump); }, 790); } } }
代码优化与游戏体验提升建议
作为经常折腾前端小游戏的开发者,我从几个维度整理了这些实用的优化方向:
1. 代码结构与可维护性升级
- 缓存DOM元素:现在每次操作都重复查询
$('#character')、$('.box'),这会浪费性能。建议初始化时一次性缓存所有常用元素:const $character = $('#character'); const $allBoxes = $('.box'); const $traps = $('#trap1, #trap2'); const $endPoint = $('#end'); - 整合碰撞逻辑:目前
collision和colCheck两个函数功能重叠,可合并成一个返回碰撞状态+方向的统一函数,减少冗余代码。 - 集中管理常量:把角色速度、跳跃参数、碰撞偏移量这些硬编码数值放到一个配置对象里,后续调整数值会方便很多:
const GAME_SETTINGS = { moveSpeed: 40, jumpStep: 8, jumpDuration: 790, collisionAdjust: 15, gravity: 2 }; - 模块化拆分:把输入处理、碰撞检测、角色移动、游戏状态拆成独立模块,比如写个
InputManager处理键盘事件,CollisionSystem负责碰撞判断,代码会更清晰,后续扩展功能也更轻松。
2. 性能优化:减少不必要的开销
- 用
requestAnimationFrame替代定时器:现在setInterval(col, 5)的间隔太密集,会加重CPU负担。换成requestAnimationFrame的游戏循环,能和浏览器刷新同步,性能更优:function gameLoop() { handleCollisions(); handleMovement(); requestAnimationFrame(gameLoop); } // 启动游戏循环 gameLoop(); - 减少DOM操作频次:频繁调用
css()修改样式会触发多次重排重绘,建议先计算好角色位置,一次性更新样式;或者用transform: translate()代替top/left,transform的性能更好,不会触发布局重排。
3. 游戏体验:让手感更丝滑
- 加入物理效果优化跳跃手感:当前跳跃是匀速上升,手感生硬。可以加入重力加速度,让跳跃有“先快后慢上升,再加速下落”的过程,更符合真实物理直觉:
let yVelocity = 0; const gravity = 0.5; const jumpForce = -12; let isOnGround = false; function updateVerticalPosition() { yVelocity += gravity; const newTop = $character.offset().top + yVelocity; // 碰撞检测后调整位置,更新isOnGround状态 $character.css('top', newTop); } // 跳跃时触发: if (isOnGround) { yVelocity = jumpForce; isOnGround = false; } - 优化输入处理:现在用
animate处理移动,快速按键会导致动画队列堆积。可以用按键状态跟踪,在游戏循环里实时处理移动:const keys = {}; document.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true); document.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false); function handleMovement() { const currentLeft = $character.position().left; if (keys['a'] && currentLeft > 0) { $character.css('left', currentLeft - 5); } if (keys['d']) { $character.css('left', currentLeft + 5); } if (keys['w'] && isOnGround) { yVelocity = jumpForce; isOnGround = false; } } - 补充边界与反馈:现在只限制了左边界,角色可能跑出屏幕;碰到陷阱直接刷新页面体验差,可以改成重置角色到起点;到达终点可以加个简单的庆祝动画,提升趣味性。
4. 修复冗余与小问题
- 移除重复碰撞检测:
moveCharu里重复调用了所有盒子的碰撞检测,可以把碰撞结果缓存起来,同一帧里复用。 - 修正语法小错误:
moveCharu(dir, )里多了个逗号,需要删除,避免控制台警告。 - 优化重置逻辑:碰到陷阱或通关时,不用刷新页面,直接重置角色位置和游戏状态即可。
内容的提问来源于stack exchange,提问作者semih ezen
相关产品推荐
相关产品推荐

