记录--Canvas实现打飞字游戏

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

打开游戏界面,看到一个画面简洁、却又富有挑战性的游戏。屏幕上,有一个白色的矩形框,里面不断下落着各种单词,而我需要迅速地输入这些单词。如果我输入的单词与屏幕上的单词匹配,那么我就可以获得得分;如果我输入的单词错误或者时间过长,那么我就会输掉游戏。游戏的节奏非常快,每当我输入一个单词,屏幕上就会有新的单词出现,让我不能有丝毫的懈怠。

在游戏中,我不断地挑战自己,不断地提高自己的打字速度和准确性。经过一段时间的练习,我发现我的打字速度和准确性都有了显著的提高,这让我非常开心。

一、游戏介绍

打字游戏使用Canvas和JavaScript实现。游戏的核心玩法是,玩家需要在字母下落到底部之前输入相应的单词。如果玩家输入正确,就会得到相应的分数。游戏中包含了许多有趣的功能,如随机生成单词、单词下落、单词匹配、得分计算等等。此外,游戏设计还考虑到了玩家的游戏体验,如游戏难度的调整、游戏音效的设置等等。如果你喜欢挑战和打字游戏,那么这款游戏一定不容错过!

二、效果预览

体验链接:打飞字游戏 - 码上掘金 (juejin.cn)

打飞字.gif

三、实现思路

在实现游戏时,主要包括以下几个部分:

  • 随机生成单词
  • 添加新的单词
  • 更新画面
  • 画出单词
  • 处理已输入单词
  • 处理未输入单词
  • 重置游戏

具体实现可以参考代码中的注释。

1. 搭建页面结构

使用Canvas和JavaScript实现的打字游戏的HTML模板。在这个HTML模板中,我们使用了canvas元素来显示游戏画面。此外,我们还添加了一个得分标签、一个文本输入框和一个重置游戏按钮。在游戏开始时,用户需要点击文本输入框并输入单词。如果输入的单词与下落的单词匹配,则会得到相应的分数。如果下落的单词没有被输入,则游戏结束。用户可以通过点击重置游戏按钮重新开始游戏。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
    <title>Canvas打字游戏</title>
    <meta charset="UTF-8">
</head>
<body>
    <canvas id="gameCanvas" width="500" height="400"></canvas>
    <p>得分: <span id="score">0</span></p>
    <input type="text" id="userInput" autofocus>
    <button id="resetButton">重新开始</button>
</body>
</html>

2. 美化界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
canvas {
  border: 1px solid black;
}
 
body {
  display: flex;
  flex-direction: column;
  align-items: center;
}
 
#gameCanvas {
  margin: 20px;
}
 
input[type=text] {
  margin: 20px;
  font-size: 20px;
  padding: 10px;
  border: none;
  border-bottom: 2px solid gray;
}
 
#score {
  font-size: 20px;
  margin: 20px;
}
 
#resetButton {
  margin: 20px;
  font-size: 20px;
  padding: 10px;
  border: none;
  background-color: #4CAF50;
  color: white;
  border-radius: 5px;
}
 
#resetButton:hover {
  background-color: #3E8E41;
}

3. 编写JavaScript代码

对于js代码的编写,我用ES6的class语法来进行编写。使用ES6中的class语法来定义一个游戏类,能够利用class语法的面向对象特性来进行游戏逻辑的封装和组织。使用class语法可以更加清晰地表达游戏的结构和关系,将游戏的各个部分封装在一个类中,可以更加方便地管理和维护代码。

同时,使用class语法还可以更加方便地进行继承和多态的操作,方便扩展和重用代码。在实现游戏时,可能会有不同的游戏模式,或者需要对游戏进行一些特殊的调整。使用class语法可以更加便捷地扩展和修改游戏的逻辑,提高代码的可维护性和可扩展性。

还可以更加方便地进行代码的组织和管理。游戏逻辑封装在一个类中,可以更加清晰地表达游戏的结构和关系,方便代码的组织和管理。同时还可以更加方便地进行代码的测试和调试,提高代码的质量和可靠性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
class TypingGame {
  constructor() {
    this.canvas = document.getElementById("gameCanvas");
    this.context = this.canvas.getContext("2d");
    this.gameStatus = 'looping' // 游戏状态,初始值为 'looping'
    this.blinkInterval = null;
    this.score = 0 // 得分,初始值为 0
    this.wordList = [];
    this.SPEED = 1; // 字符下落速度
    this.ANGLE = Math.PI / 2;
    this.words = ['apple', 'orange', 'banana', 'pear', 'grape'];
    this.userInput = document.getElementById("userInput");
    this.resetButton = document.getElementById("resetButton");
    this.addNewWord = this.addNewWord.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
    this.resetGame = this.resetGame.bind(this);
    this.update = this.update.bind(this);
    this.drawWord = this.drawWord.bind(this);
    this.handleWordMatch = this.handleWordMatch.bind(this);
    this.handleWordMiss = this.handleWordMiss.bind(this);
    this.init();
  }
 
  /**
   * 初始化游戏
   */
  init() {
    // 随机生成一些单词
    this.generateRandomWords();
    // 绑定键盘输入事件
    this.userInput.addEventListener("keypress", this.handleKeyPress);
    // 绑定重置游戏按钮点击事件
    this.resetButton.addEventListener("click", this.resetGame);
    // 添加第一个单词
    this.addNewWord();
    // 开始游戏循环
    this.update();
  }
 
  /**
   * 随机生成一些单词
   */
  generateRandomWords() {
    for (let i = 0; i < 100; i++) {
      // 随机生成一个指定长度的单词
      const word = this.getRandomString(Math.floor(Math.random() * 7) + 3);
      this.words.push(word);
    }
  }
 
  /**
   * 随机生成一个字母
   */
  getRandomLetter() {
    const letters = "abcdefghijklmnopqrstuvwxyz";
    const index = Math.floor(Math.random() * letters.length);
    return letters[index];
  }
 
  /**
   * 随机生成一个指定长度的单词
   */
  getRandomString(length) {
    let result = "";
    for (let i = 0; i < length; i++) {
      result += this.getRandomLetter();
    }
    return result;
  }
 
  /**
   * 添加新的单词
   */
  addNewWord() {
    // 获取单词的宽度
    const wordWidth = this.context.measureText(this.getRandomWord()).width;
    const word = {
      word: this.getRandomWord(),
      x: Math.max(wordWidth, Math.random() * (this.canvas.width - wordWidth)),
      y: 0,
      angle: this.ANGLE,
    };
    this.wordList.push(word);
  }
 
  /**
   * 随机获取一个单词
   */
  getRandomWord() {
    const index = Math.floor(Math.random() * this.words.length);
    return this.words[index];
  }
 
  /**
   * 更新画面
   */
  update() {
    if (this.gameStatus !== 'looping') return;
    // 清空画布
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.wordList.forEach((word, i) => {
      word.y += this.SPEED;
      word.x += Math.sin(word.angle);
      word.angle += Math.random() * 0.1 - 0.05;
      const x = word.x - this.context.measureText(word.word).width / 2;
      // 画出单词
      this.drawWord(word.word, x, word.y);
 
      if (word.x < 0 || word.x > this.canvas.width) {
        word.angle = -word.angle;
      }
 
      if (word.y > this.canvas.height) {
        // 处理未输入单词
        this.handleWordMiss(word);
        this.wordList.splice(i, 1);
        // 添加新的单词
        this.addNewWord();
      }
    });
    // 请求下一帧动画
    requestAnimationFrame(this.update);
  }
 
  /**
   * 画出单词
   */
  drawWord(word, x, y) {
    this.context.font = "30px Arial";
    this.context.fillText(word, x, y);
  }
 
  /**
   * 处理已输入单词
   */
  handleKeyPress(event) {
    if (event.keyCode === 13) {
      const userWord = this.userInput.value;
      this.userInput.value = "";
 
      this.wordList.forEach((word, idx) => {
        if (word.word === userWord) {
          // 处理已输入单词
          this.handleWordMatch(word, idx);
        }
      });
    }
  }
 
  /**
   * 处理已输入单词
   */
  handleWordMatch(word, idx) {
    // 增加得分
    this.score++;
    // 更新得分显示
    document.getElementById("score").innerText = this.score;
    const x = word.x - this.context.measureText(word.word).width / 2;
    const y = word.y;
    let isWhite = true;
    let blinkCount = 0;
    // 单词闪烁
    this.blinkInterval = setInterval(() => {
      if (isWhite) {
        this.context.fillStyle = "white";
      } else {
        this.context.fillStyle = "black";
      }
      this.context.fillText(word.word, x, y);
      isWhite = !isWhite;
      blinkCount++;
      if (blinkCount >= 10) {
        this.context.fillStyle = "black";
        this.context.fillText(word.word, x, y);
        this.wordList.splice(idx, 1)
        // 添加新的单词
        this.addNewWord()
        clearInterval(this.blinkInterval);
      }
    }, 100);
  }
 
  /**
   * 处理未输入单词
   */
  handleWordMiss(word) {
    if (word.y > this.canvas.height) {
      clearInterval(this.blinkInterval);
      this.gameStatus = 'pause';
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      this.context.font = "30px Arial";
      let text =['你输了,你这个菜鸡,','恭喜你,虽败犹荣,','真棒,我的宝子厉害,']
      let textSay=this.score>15?this.score>50?text[2]:text[1]:text[0];
      this.context.fillText(`${textSay}分数${this.score}分`, this.canvas.width / 2 - 180, this.canvas.height / 2);
 
    }
  }
 
  /**
   * 重置游戏
   */
  resetGame() {
    this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
    // 开始游戏循环
    requestAnimationFrame(this.update);
    clearInterval(this.blinkInterval);
    this.gameStatus='looping';
    this.score = 0;
    // 更新得分显示
    document.getElementById("score").innerText = this.score;
    this.wordList = [];
    // 添加新的单词
    this.addNewWord();
  }
}
 
const typingGame = new TypingGame();

TypingGame类是整个游戏的核心。在constructor方法中,首先初始化了一些游戏状态和相关的变量,然后调用了init方法,对游戏进行初始化。在init方法中,定义了一些事件处理方法,如键盘输入事件处理方法、重置游戏按钮点击事件处理方法等等。在init方法中,还调用了addNewWord方法,添加了第一个单词,并且开始游戏循环。在update方法中,主要是更新画面的逻辑,如清空画布、画出单词、处理已输入单词、处理未输入单词等等。在resetGame方法中,主要是重置游戏的状态,如清空画布、得分归零、添加新的单词等等。

整个游戏的实现比较简单,主要是依赖于Canvas和JavaScript。游戏中使用了一些Canvas的API,如context.fillText()方法、context.clearRect()方法等等,同时还使用了一些JavaScript的语言特性,如类、箭头函数等等。如果你对游戏的实现过程感兴趣,可以参考代码中的注释,了解游戏中每个方法的具体实现细节。

四、写在最后

Canvas和JavaScript看似平凡无奇,却能够创造出令人惊叹的数字世界。在这个数字化时代,掌握这些工具已经成为了一种竞争优势。本篇文章将带领读者一起探索Canvas和JavaScript的世界,通过实现一个打字游戏,领略这些工具的神奇之处。

游戏的实现并不复杂,但却需要运用许多Canvas的API和JavaScript的语言特性。通过随机生成单词、让单词下落、根据用户输入判断单词是否匹配等等,我们成功实现了一个简单而有趣的游戏。在实现游戏的过程中,我们也学习了一些Canvas的API和JavaScript的语言特性,例如类、箭头函数等等。

本文转载于:

https://juejin.cn/post/7217704608034193468

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

posted @   林恒  阅读(145)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
欢迎阅读『记录--Canvas实现打飞字游戏』
点击右上角即可分享
微信分享提示