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

如何在HTML游戏中基于LocalStorage实现本地高分保存系统?

实现基于LocalStorage的本地高分保存功能

我看到你已经有了highScore函数的雏形,但还需要把它和游戏的得分逻辑、碰撞结束流程联动起来。下面是完整的修改方案,一步步帮你搞定这个功能:

1. 完善得分计算与显示逻辑

你的现有代码里myScore组件只是初始化了,但没有实时更新得分。我们可以用游戏的帧号作为基础得分(每过一帧加1),在updateGameArea里实时更新:

function updateGameArea() {
  // ... 原有碰撞检测代码 ...

  // 实时更新当前得分
  myScore.text = "Score: " + myGameArea.frameNo;
  
  // ... 原有绘制元素代码 ...
}

2. 碰撞时自动更新高分

当游戏因碰撞结束时,需要把当前得分和本地保存的高分对比,如果当前得分更高,就更新LocalStorage里的记录:

function updateGameArea() {
  for (i = 0; i < myObstacles.length; i += 1) {
    if (myGamePiece.crashWith(myObstacles[i])) {
      const currentScore = myGameArea.frameNo;
      const savedHighScore = highScore();
      
      // 对比得分并更新
      if (currentScore > savedHighScore) {
        highScore(currentScore);
        alert(`新纪录!你的得分是 ${currentScore},之前的高分是 ${savedHighScore}`);
      } else {
        alert(`游戏结束!你的得分是 ${currentScore},当前高分是 ${savedHighScore}`);
      }
      
      myGameArea.stop();
      document.getElementsByClassName("resetButton")[0].innerHTML='<button onclick="location.reload();" style="height:27px;width:815px">Restart Game</button>';
      return;
    }
  }

  // ... 原有其他代码 ...
}

3. 优化highScore函数的健壮性

你的初始函数已经不错,我调整成更规范的写法,避免潜在的异常:

function highScore(score) {
  // 用getItem/setItem更规范,避免属性访问的潜在问题
  let saved = localStorage.getItem('highScore');
  // 处理首次访问或保存值无效的情况
  saved = saved ? parseFloat(saved) : 0;
  
  if (!isNaN(score)) {
    // 只有传入有效数字时才更新本地存储
    localStorage.setItem('highScore', score.toString());
    return score;
  }
  
  // 确保返回的是有效数字
  return isNaN(saved) ? 0 : saved;
}

4. 游戏初始化时显示当前高分

可以在游戏开始时就展示当前的高分,提升用户感知:

function startGame() {
  // ... 原有初始化代码 ...
  
  // 添加高分显示组件
  myHighScoreDisplay = new component("30px", "Algerian", "gold", 600, 30, "text");
  myHighScoreDisplay.text = "High Score: " + highScore();
  
  myGameArea.start();
}

然后在updateGameArea里保持高分显示的更新:

function updateGameArea() {
  // ... 原有代码 ...
  
  // 实时更新高分显示
  myHighScoreDisplay.text = "High Score: " + highScore();
  
  // 绘制所有组件
  myBackground.update();
  myGamePiece.update();
  myScore.update();
  myHighScoreDisplay.update();
  
  // ... 原有障碍物绘制代码 ...
}

5. 修复HTML按钮的语法错误

你的按钮标签存在嵌套错误和未闭合的问题,修复后:

<button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)" style="height:27px;width:815px">Click to Accelerate</button>
<div class="resetButton"></div>
<button type="button" onclick="alert('Owner: Landon Sturtevant')" style="height:27px;width:815px">Credits</button>
<button type="button" onclick="alert('Click OK to Continue')" style="height:27px;width:815px">Pause</button>
<button type="button" onclick="alert('High Score is ' + highScore());" style="height:27px;width:815px">HighScore</button>

完整修改后代码

我把所有修改整合到你的代码里,还补全了被截断的障碍物部分:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <style>
        canvas {
            border:7px solid #00aaff;
            background-color: #00aaff;
        }
    </style>
</head>
<body onload="startGame()">
<br>
<button onmousedown="accelerate(-0.2)" onmouseup="accelerate(0.05)" style="height:27px;width:815px">Click to Accelerate</button>
<div class="resetButton"></div>
<button type="button" onclick="alert('Owner: Landon Sturtevant')" style="height:27px;width:815px">Credits</button>
<button type="button" onclick="alert('Click OK to Continue')" style="height:27px;width:815px">Pause</button>
<button type="button" onclick="alert('High Score is ' + highScore());" style="height:27px;width:815px">HighScore</button>

<script>
function highScore(score) {
    let saved = localStorage.getItem('highScore');
    saved = saved ? parseFloat(saved) : 0;
    
    if (!isNaN(score)) {
        localStorage.setItem('highScore', score.toString());
        return score;
    }
    
    return isNaN(saved) ? 0 : saved;
}

var myGamePiece;
var myBackground;
var myObstacles = [];
var myScore;
var myHighScoreDisplay;
var gameSpeed = 1;

function startGame() {
    myBackground = new component(800, 380,"http://static.giantbomb.com/uploads/original/29/297846/2789895-cool+space+backgrounds+desktop+hd+wallpaper.jpg", 0, 0, "image");
    myGamePiece = new component(50, 50, "http://images.tourismholdings.com/public/2016/02/56bd3c714ecc6.png", 35, 200, "image");
    myScore = new component("30px", "Algerian", "turquoise", 15, 30, "text");
    myHighScoreDisplay = new component("30px", "Algerian", "gold", 600, 30, "text");
    myHighScoreDisplay.text = "High Score: " + highScore();
    myGameArea.start();
}

var myGameArea = {
    canvas : document.createElement("canvas"),
    start : function() {
        this.canvas.width = 800;
        this.canvas.height = 290;
        this.context = this.canvas.getContext("2d");
        document.body.insertBefore(this.canvas, document.body.childNodes[0]);
        this.frameNo = 0;
        this.interval = setInterval(updateGameArea, 20);
    },
    clear : function() {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    },
    stop : function() {
        clearInterval(this.interval);
    }
}

function component(width, height, color, x, y, type) {
    if (type == "image") {
        this.image = new Image();
        this.image.src = color;
    }
    this.type = type;
    this.width = width;
    this.height = height;
    this.speedX = 0;
    this.speedY = 0;
    this.gravity = 0.05;
    this.gravitySpeed = 0;
    this.x = x;
    this.y = y;
    this.text = ""; // 新增text属性用于文本组件
    
    this.update = function() {
        ctx = myGameArea.context;
        if (type == "image") {
            ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
        } else if (this.type == "text") {
            ctx.font = this.width + " " + this.height;
            ctx.fillStyle = color;
            ctx.fillText(this.text, this.x, this.y);
        }
    }
    
    this.newPos = function() {
        this.gravitySpeed += this.gravity;
        this.x += this.speedX;
        this.y += this.speedY + this.gravitySpeed;
        this.hitBottom();
    }
    
    this.hitBottom = function() {
        var rockbottom = myGameArea.canvas.height - this.height;
        if (this.y > rockbottom) {
            this.y = rockbottom;
        }
    }
    
    this.crashWith = function(otherobj) {
        var myleft = this.x;
        var myright = this.x + (this.width);
        var mytop = this.y;
        var mybottom = this.y + (this.height);
        var otherleft = otherobj.x;
        var otherright = otherobj.x + (otherobj.width);
        var othertop = otherobj.y;
        var otherbottom = otherobj.y + (otherobj.height);
        var crash = true;
        if ((mybottom < othertop) || (mytop > otherbottom) || (myright < otherleft) || (myleft > otherright)) {
            crash = false;
        }
        return crash;
    }
}

function everyinterval(n) {
    if ((myGameArea.frameNo / n) % 1 == 0) {return true;}
    return false;
}

function accelerate(n) {
    myGamePiece.gravity = n;
}

function updateGameArea() {
    var x, height, gap, minHeight, maxHeight, minGap, maxGap;
    for (i = 0; i < myObstacles.length; i += 1) {
        if (myGamePiece.crashWith(myObstacles[i])) {
            const currentScore = myGameArea.frameNo;
            const savedHighScore = highScore();
            
            if (currentScore > savedHighScore) {
                highScore(currentScore);
                alert(`新纪录!你的得分是 ${currentScore},之前的高分是 ${savedHighScore}`);
            } else {
                alert(`游戏结束!你的得分是 ${currentScore},当前高分是 ${savedHighScore}`);
            }
            
            myGameArea.stop()
            document.getElementsByClassName("resetButton")[0].innerHTML='<button onclick="location.reload();" style="height:27px;width:815px">Restart Game</button>';
            return;
        }
    }
    
    myGameArea.clear();
    myGameArea.frameNo += 1;
    
    // 更新得分与高分显示
    myScore.text = "Score: " + myGameArea.frameNo;
    myHighScoreDisplay.text = "High Score: " + highScore();
    
    if (myGameArea.frameNo == 1 || everyinterval(150)) {
        x = myGameArea.canvas.width;
        minHeight = 20;
        maxHeight = 200;
        height = Math.floor(Math.random()*(maxHeight-minHeight+1)+minHeight);
        minGap = 150;
        maxGap = 250;
        gap = Math.floor(Math.random()*(maxGap-minGap+1)+minGap);
        myObstacles.push(new component(15, height, "https://orig00.deviantart.net/bb11/f/2014/127/f/e/hexagon_wallpaper___version_3_by_designedby_jack-d7hh9xo.png", x, 0, "image"));
        myObstacles.push(new component(15, x - height - gap, "https://orig00.deviantart.net/bb11/f/2014/127/f/e/hexagon_wallpaper___version_3_by_designedby_jack-d7hh9xo.png", x, height + gap, "image"));
    }
    
    // 移动障碍物
    for (i = 0; i < myObstacles.length; i += 1) {
        myObstacles[i].x -= gameSpeed;
        myObstacles[i].update();
    }
    
    myBackground.update();
    myGamePiece.newPos();
    myGamePiece.update();
    myScore.update();
    myHighScoreDisplay.update();
}
</script>
</body>
</html>

关键说明

  • 用localStorage.getItem/setItem替代直接属性访问,避免潜在的命名冲突和异常。
  • 游戏结束时自动对比得分,更新后给用户明确提示。
  • 实时显示当前得分和高分,让用户随时了解进度。
  • 修复了原有HTML按钮的语法错误,确保页面正常渲染。

内容的提问来源于stack exchange,提问作者Landon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:51