【花雕动手做】基于 Kitronik 可编程开发板之方块 Boss

2025-09-121620
AI 快速预览详细 收起
Kitronik ARCADE是一款由英国教育科技公司Kitronik精心打造的可编程游戏机开发板,专为编程教学与创客实践而设计。该设备原生支持微软的MakeCode Arcade平台,用户可通过图形化或JavaScript编程方式,轻松创建、下载并运行复古风格的街机游戏。它集成了彩色LCD显示屏、方向控制键、功能按键、蜂鸣器和震动马达等交互组件,提供完整的游戏输入输出体验。无论是初学者进行编程启蒙,还是创客群体开发交互式作品,Kitronik ARCADE都能作为理想的硬件载体,助力创意实现。
Kitronik ARCADE 是一款由英国教育科技公司 Kitronik 精心打造的可编程游戏机开发板,专为编程教学与创客实践而设计。该设备原生支持微软的 MakeCode Arcade 平台,用户可通过图形化或 JavaScript 编程方式,轻松创建、下载并运行复古风格的街机游戏。

它集成了彩色 LCD 显示屏、方向控制键、功能按键、蜂鸣器和震动马达等交互组件,提供完整的游戏输入输出体验。无论是初学者进行编程启蒙,还是创客群体开发交互式作品,Kitronik ARCADE 都能作为理想的硬件载体,助力创意实现。

凭借其开源友好、易于上手、兼容性强等特点,该开发板广泛应用于中小学编程课程、创客工作坊、游戏开发教学以及个人项目原型设计,深受教育者与技术爱好者的喜爱。

【花雕动手做】基于 Kitronik 可编程开发板之方块 Boss图2

【花雕动手做】基于 Kitronik 可编程开发板之方块 Boss图1

【花雕动手做】基于 Kitronik 可编程开发板之方块 Boss图3

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(4)
驴友花雕
驴友花雕
作者
通过模拟器,调试与模拟运行

实验场景记录


驴友花雕
驴友花雕
作者
图形编程参考实验程序

驴友花雕
驴友花雕
作者
这段代码是一个用 MakeCode Arcade 编写的完整 Boss 战游戏框架,名为 “方块 Boss”。它融合了玩家控制、Boss AI、弹幕系统、生命条动画、阶段切换等机制,构建出一个具有挑战性的战斗场景。下面是从专业角度对其结构与逻辑的详细解读:
一、游戏核心机制概览

二、关键逻辑模块解析
1、玩家与 Boss 初始化
python
[code]mySprite = sprites.create(..., SpriteKind.player)
boss = sprites.create(..., SpriteKind.enemy)[/code]
玩家角色使用 controller.move_sprite() 控制移动
Boss 初始位置为屏幕外,随后通过 preSetBossPosition() 移入战场
2、Boss 生命条加载动画
python
[code]on_update_interval5()[/code]
每 100ms 更新一次生命条图像,逐步填充
加载完成后 started = True,正式进入战斗状态
3、玩家攻击 Boss
python
[code]controller.A.on_event(...): shootBulletFromSprite(mySprite, 200, -90)
sprites.on_overlap(SpriteKind.enemy, SpriteKind.PlayerShot, on_on_overlap)[/code]
玩家按 A 键发射子弹
子弹击中 Boss 后:
得分 +20
Boss 生命值减少
更新生命条图像
每损失 12 点生命触发一次位置重置
4、Boss 攻击模式切换
python
[code]bossProgress = 1 → nonSpell1()
bossProgress = 2 → spell1()
bossProgress = 3 → nonSpell2()
bossProgress = 4 → spell2()[/code]
每种攻击模式通过定时器触发不同弹幕函数
nonSpell1():环形弹幕
spell1():追踪弹
nonSpell2():双层环形弹幕
spell2():扇形弹幕
5、Boss 移动逻辑
python
[code]moveSpriteRandom(boss, 40, 8, 60)[/code]
每 2.5 秒随机移动一次
使用 moveSprite() 或 moveSpriteInTime() 实现平滑移动
到达目标位置后触发阶段推进逻辑
6、玩家受伤逻辑
python
[code]sprites.on_overlap(SpriteKind.player, SpriteKind.projectile, on_on_overlap2)[/code]
玩家被 Boss 弹幕击中:
生命值 -1
屏幕震动
播放音效
三、弹幕生成机制
shootBulletFromSprite(source, speed, angle)
使用三角函数计算速度向量
根据发射者类型设置不同图像与种类
自动销毁,避免资源堆积
enemyShootAimingPlayer(sprite, speed, spread)
计算玩家与 Boss 的角度
添加随机偏移实现“散射追踪弹”。
驴友花雕
驴友花雕
作者
作为学习、练习与尝试,这里创建一个方块 Boss 的小游戏。
打开网页版:https://arcade.makecode.com/,设置项目名称:方块 Boss
MicroPython实验参考代码
[code]@namespace
class SpriteKind:
PlayerShot = SpriteKind.create()
LifeBar = SpriteKind.create()
def moveSpriteInTime(sprite: Sprite, x: number, y: number, t: number):
global globalX, globalY, dx, dy
globalX = x
globalY = y
dx = x - sprite.x
dy = y - sprite.y
sprite.set_velocity(dx / t, dy / t)
def on_on_overlap(sprite2, otherSprite):
global bossLife
if started:
info.change_score_by(20)
bossLife += -1
music.play_tone(208, music.beat(BeatFraction.EIGHTH))
lifeBarPic.fill_rect(bossLife * 2, 0, 96 - bossLife * 2, 5, 15)
lifeBar.set_image(lifeBarPic)
if bossLife <= 0:
game.over(True)
elif bossLife % 12 == 0:
preSetBossPosition(80, 30)
otherSprite.destroy()
sprites.on_overlap(SpriteKind.enemy, SpriteKind.PlayerShot, on_on_overlap)
def spell1():
enemyShootAimingPlayer(boss, 90, 5)
def on_b_released():
controller.move_sprite(mySprite)
controller.B.on_event(ControllerButtonEvent.RELEASED, on_b_released)
def moveSpriteRandom(sprite3: Sprite, yLowerBound: number, outerBound: number, v: number):
moveSprite(sprite3,
randint(outerBound, scene.screen_width() - outerBound),
randint(outerBound, yLowerBound),
v)
def on_a_pressed():
shootBulletFromSprite(mySprite, 200, -90)
controller.A.on_event(ControllerButtonEvent.PRESSED, on_a_pressed)
def nonSpell1():
global offset
index2 = 0
while index2 <= MAX - 1:
shootBulletFromSprite(boss, 60, 360 / MAX * index2 + offset)
index2 += 1
offset += 13
def spell2():
global offset
for index in range(5):
shootBulletFromSprite(boss, 60, offset + index * 30)
offset += 23
def on_on_overlap2(sprite4, otherSprite2):
info.change_life_by(-1)
scene.camera_shake(3, 200)
music.play_tone(139, music.beat(BeatFraction.EIGHTH))
otherSprite2.destroy()
sprites.on_overlap(SpriteKind.player, SpriteKind.projectile, on_on_overlap2)
def on_b_pressed():
controller.move_sprite(mySprite, 50, 50)
controller.B.on_event(ControllerButtonEvent.PRESSED, on_b_pressed)
def preSetBossPosition(x2: number, y2: number):
global started, ready, offset
started = False
ready = False
offset = 0
moveSpriteInTime(boss, x2, y2, 1)
def moveSpriteRandomFixedTime(sprite5: Sprite, yLowerBound2: number, outerBound2: number, u: number):
moveSpriteInTime(sprite5,
randint(outerBound2, scene.screen_width() - outerBound2),
randint(outerBound2, yLowerBound2),
u)
def nonSpell2():
index3 = 0
while index3 <= MAX - 1:
shootBulletFromSprite(boss, 60, 360 / MAX * index3 + offset)
shootBulletFromSprite(boss, 100, 360 / MAX * (index3 + 0.5) + offset)
index3 += 1
def shootBulletFromSprite(sourceSprite: Sprite, speed: number, angle: number):
global projectile
projectile = sprites.create_projectile_from_sprite(img("""
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
"""),
sourceSprite,
speed * Math.cos(angle / 57.3),
speed * Math.sin(angle / 57.3))
projectile.set_flag(SpriteFlag.AUTO_DESTROY, True)
if sourceSprite.kind() == SpriteKind.player:
projectile.set_kind(SpriteKind.PlayerShot)
projectile.set_image(img("""
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . 5 5 . . . . . . .
. . . . . . 5 4 4 5 . . . . . .
. . . . . 5 4 2 2 4 5 . . . . .
. . . . . 5 4 2 2 4 5 . . . . .
. . . . . . 5 4 4 5 . . . . . .
. . . . . . . 5 5 . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
"""))
else:
projectile.set_image(img("""
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . 9 9 . . . . . . .
. . . . . . 9 6 6 9 . . . . . .
. . . . . 9 6 8 8 6 9 . . . . .
. . . . . 9 6 8 8 6 9 . . . . .
. . . . . . 9 6 6 9 . . . . . .
. . . . . . . 9 9 . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
"""))
def moveSprite(sprite6: Sprite, x3: number, y3: number, w: number):
global globalX, globalY, dx, dy, speed3
globalX = x3
globalY = y3
dx = x3 - sprite6.x
dy = y3 - sprite6.y
speed3 = Math.sqrt(dx * dx + dy * dy)
if speed3 != 0:
sprite6.set_velocity(dx / speed3 * w, dy / speed3 * w)
def enemyShootAimingPlayer(sprite7: Sprite, speed2: number, spread: number):
shootBulletFromSprite(sprite7,
speed2,
Math.atan2(mySprite.y - sprite7.y, mySprite.x - sprite7.x) * 57.3 + randint(0 - spread, spread))
lifeBarProgress = 0
bossProgress = 0
speed3 = 0
projectile: Sprite = None
ready = False
started = False
dy = 0
dx = 0
globalY = 0
globalX = 0
MAX = 0
offset = 0
lifeBar: Sprite = None
lifeBarPic: Image = None
boss: Sprite = None
mySprite: Sprite = None
bossLife = 0
bossLife = 48
info.set_life(20)
info.set_score(0)
music.set_volume(20)
mySprite = sprites.create(img("""
. . . . . . f f f f . . . . . .
. . . . f f e e e e f f . . . .
. . . f e e e f f e e e f . . .
. . f f f f f 2 2 f f f f f . .
. . f f e 2 e 2 2 e 2 e f f . .
. . f e 2 f 2 f f 2 f 2 e f . .
. . f f f 2 2 e e 2 2 f f f . .
. f f e f 2 f e e f 2 f e f f .
. f e e f f e e e e f e e e f .
. . f e e e e e e e e e e f . .
. . . f e e e e e e e e f . . .
. . e 4 f f f f f f f f 4 e . .
. . 4 d f 2 2 2 2 2 2 f d 4 . .
. . 4 4 f 4 4 4 4 4 4 f 4 4 . .
. . . . . f f f f f f . . . . .
. . . . . f f . . f f . . . . .
"""),
SpriteKind.player)
mySprite.set_position(80, 105)
mySprite.set_flag(SpriteFlag.STAY_IN_SCREEN, True)
controller.move_sprite(mySprite)
boss = sprites.create(img("""
........................
........................
........................
........................
..........ffff..........
........ff1111ff........
.......fb111111bf.......
.......f11111111f.......
......fd11111111df......
......fd11111111df......
......fddd1111dddf......
......fbdbfddfbdbf......
......fcdcf11fcdcf......
.......fb111111bf.......
......fffcdb1bdffff.....
....fc111cbfbfc111cf....
....f1b1b1ffff1b1b1f....
....fbfbffffffbfbfbf....
.........ffffff.........
...........fff..........
........................
........................
........................
........................
"""),
SpriteKind.enemy)
boss.set_position(-16, -16)
lifeBarPic = image.create(96, 5)
lifeBar = sprites.create(lifeBarPic, SpriteKind.LifeBar)
lifeBar.set_position(80, 5)
lifeBar.set_flag(SpriteFlag.GHOST, True)
offset = 0
MAX = 10
bossCanMove = True
preSetBossPosition(80, 30)
def on_on_update():
global bossProgress, bossCanMove, MAX, ready
if abs(boss.x - globalX) + abs(boss.y - globalY) <= 2:
boss.set_velocity(0, 0)
if not (ready):
bossProgress += 1
if bossProgress == 2:
bossCanMove = False
else:
if bossProgress == 2:
MAX = 8
bossCanMove = True
ready = True
game.on_update(on_on_update)
def on_update_interval():
if started:
if bossProgress == 3:
nonSpell2()
game.on_update_interval(750, on_update_interval)
def on_update_interval2():
if started and bossCanMove:
moveSpriteRandom(boss, 40, 8, 60)
game.on_update_interval(2500, on_update_interval2)
def on_update_interval3():
if started:
if bossProgress == 2:
spell1()
else:
if bossProgress == 4:
spell2()
game.on_update_interval(150, on_update_interval3)
def on_update_interval4():
if started:
if bossProgress == 1:
nonSpell1()
game.on_update_interval(500, on_update_interval4)
def on_update_interval5():
global lifeBarProgress, started
if ready and not (started):
if lifeBarProgress < 4:
lifeBarPic.fill_rect(24 * lifeBarProgress, 0, 24, 5, 14 - lifeBarProgress % 2 * 6)
lifeBarPic.fill_rect(24 * lifeBarProgress, 1, 24, 3, lifeBarProgress % 2 * 5 + 4)
lifeBar.set_image(lifeBarPic)
lifeBarProgress += 1
else:
started = True
game.on_update_interval(100, on_update_interval5)[/code]
- 没有更多了 -