You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:51:32