Kitronik ARCADE 是一款由英国教育科技公司 Kitronik 精心打造的可编程游戏机开发板,专为编程教学与创客实践而设计。该设备原生支持微软的 MakeCode Arcade 平台,用户可通过图形化或 JavaScript 编程方式,轻松创建、下载并运行复古风格的街机游戏。
它集成了彩色 LCD 显示屏、方向控制键、功能按键、蜂鸣器和震动马达等交互组件,提供完整的游戏输入输出体验。无论是初学者进行编程启蒙,还是创客群体开发交互式作品,Kitronik ARCADE 都能作为理想的硬件载体,助力创意实现。
凭借其开源友好、易于上手、兼容性强等特点,该开发板广泛应用于中小学编程课程、创客工作坊、游戏开发教学以及个人项目原型设计,深受教育者与技术爱好者的喜爱。


作为学习、练习与尝试,这里创建一个Boss 冲刺的小游戏。
打开网页版:https://arcade.makecode.com/,设置项目名称:Boss 冲刺
MicroPython实验代码
@namespace
class SpriteKind:
Boss = SpriteKind.create()
BossProjectile = SpriteKind.create()
Camera = SpriteKind.create()
"""
BERTHA
"""
"""
DARYL
"""
def createBoss(current: number):
global bossSpeed, bossHealth, timeBetweenBossFire, timeBetweenBossMove, theBoss, cameraSpeed, lastBossFireTime, bossHealthThreshold, bossLocations, currentFireIndex, darylHorizontalSpeed
if current == 0:
bossSpeed = 50
bossHealth = 100
timeBetweenBossFire = 100
timeBetweenBossMove = 2000
theBoss = sprites.create(img("""
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
cccccccccccccccccccccccc
"""),
SpriteKind.Boss)
theBoss.set_position(145, 58)
theBoss.set_velocity(0, 50)
resetHealth()
elif current == 1:
if bossHealth == 0:
bossHealth = 100
resetHealth()
cameraSpeed = 20
theBoss = sprites.create(img("""
b b b b b
b b b b b
b b b b b
b b b b b
b b b b b
"""),
SpriteKind.Boss)
theBoss.set_flag(SpriteFlag.GHOST_THROUGH_WALLS, True)
theBoss.set_position(12, 59)
lastBossFireTime = game.runtime()
animation.run_image_animation(theBoss,
[img("""
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
"""),
img("""
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
"""),
img("""
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
"""),
img("""
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
"""),
img("""
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
"""),
img("""
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
"""),
img("""
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
6666666666666666
""")],
150,
False)
statusbar.set_color(6, 1)
initialize_camera()
elif current == 2:
bossHealth = 50
bossHealthThreshold = bossHealth / 3
tiles.set_tilemap(tilemap("""
level6
"""))
tiles.place_on_tile(thePlayer, tiles.get_tile_location(2, 2))
bossLocations = tiles.get_tiles_by_type(assets.tile("""
tile3
"""))
currentFireIndex = 0
lastBossFireTime = game.runtime()
statusbar.set_color(4, 1)
resetHealth()
initialize_chase()
elif current == 3:
bossHealth = 100
darylHorizontalSpeed = 60
tiles.set_tilemap(tilemap("""
level7
"""))
theBoss = sprites.create(assets.image("""
daryl
"""), SpriteKind.Boss)
theBoss.ay = gravity
theBoss.right = 155
statusbar.set_color(8, 1)
resetHealth()
else:
create_final_boss()
def initialize_chase():
global bossSpawn, theBoss, lastBossFireTime
bossSpawn = bossLocations.shift()
theBoss = sprites.create(img("""
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
5 5 5 5 5 5 5 5 5 5 5
"""),
SpriteKind.Boss)
tiles.place_on_tile(theBoss, bossSpawn)
animation.run_image_animation(theBoss,
assets.animation("""
chase animation
"""),
150,
True)
theBoss.lifespan = 4000
bossLocations.append(bossSpawn)
lastBossFireTime += 2000
def clearStage():
theBoss.destroy()
for value in sprites.all_of_kind(SpriteKind.BossProjectile):
value.destroy()
for value2 in sprites.all_of_kind(SpriteKind.projectile):
value2.destroy()
for value22 in sprites.all_of_kind(SpriteKind.Camera):
value22.destroy()
def darylThrow(dir2: number):
global projectile
theBoss.say("Daryl Throw!", 500)
pause(500)
projectile = sprites.create_projectile_from_sprite(assets.image("""
daryl projectile
"""),
theBoss,
2 * (dir2 * darylHorizontalSpeed),
0)
projectile.set_kind(SpriteKind.BossProjectile)
projectile = sprites.create_projectile_from_sprite(assets.image("""
daryl projectile
"""),
theBoss,
2 * (dir2 * darylHorizontalSpeed),
-74)
projectile.set_kind(SpriteKind.BossProjectile)
pause(500)
def resetHealth():
statusbar.max = bossHealth
statusbar.value = bossHealth
def darylDash(dir3: number):
theBoss.say("Daryl Dash!", 500)
pause(500)
theBoss.vx = 2 * (dir3 * darylHorizontalSpeed)
pause(200)
def on_on_overlap(sprite, otherSprite):
global bossHealth, timeBetweenBossMove, bossSpeed
sprite.destroy(effects.disintegrate, 10)
bossHealth += -1
statusbar.value += -1
if bossHealth == 0:
next_boss()
elif bossHealth == 50 and currentBoss == 0:
timeBetweenBossMove = 1000
bossSpeed = 100
sprites.on_overlap(SpriteKind.projectile, SpriteKind.Boss, on_on_overlap)
def on_a_pressed():
if thePlayer.is_hitting_tile(CollisionDirection.BOTTOM):
thePlayer.vy = jumpVelocity
controller.A.on_event(ControllerButtonEvent.PRESSED, on_a_pressed)
# CHASE
def on_on_destroyed(sprite2):
if currentBoss == 2 and bossHealth > 0:
initialize_chase()
sprites.on_destroyed(SpriteKind.Boss, on_on_destroyed)
def darylJump(dir4: number):
theBoss.say("Daryl Jump!", 500)
pause(500)
theBoss.vy = Math.random_range(1.5 * jumpVelocity, jumpVelocity)
theBoss.vx = dir4 * darylHorizontalSpeed
def on_on_overlap2(sprite3, otherSprite2):
sprite3.destroy(effects.disintegrate, 10)
info.change_life_by(-1)
sprites.on_overlap(SpriteKind.BossProjectile, SpriteKind.player, on_on_overlap2)
def initialize_camera():
global camera_target
camera_target = sprites.create(img("""
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . 3 3 . . . . . . .
. . . . . . . 3 3 . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
"""),
SpriteKind.Camera)
camera_target.set_flag(SpriteFlag.GHOST, True)
camera_target.set_flag(SpriteFlag.INVISIBLE, True)
camera_target.set_velocity(cameraSpeed, 0)
scene.camera_follow_sprite(camera_target)
def on_overlap_tile(sprite4, location):
clearStage()
createBoss(currentBoss)
tiles.place_on_tile(thePlayer, tiles.get_tile_location(7, 4))
scene.on_overlap_tile(SpriteKind.player,
assets.tile("""
tile2
"""),
on_overlap_tile)
def on_on_overlap3(sprite5, otherSprite3):
if currentBoss == 1:
sprite5.destroy(effects.disintegrate, 10)
sprites.change_data_number_by(otherSprite3, "health", -1)
if sprites.read_data_number(otherSprite3, "health") == 8:
otherSprite3.image.replace(5, 4)
elif sprites.read_data_number(otherSprite3, "health") == 4:
otherSprite3.image.replace(4, 2)
elif sprites.read_data_number(otherSprite3, "health") == 0:
otherSprite3.destroy()
sprites.on_overlap(SpriteKind.projectile,
SpriteKind.BossProjectile,
on_on_overlap3)
def on_on_overlap4(sprite6, otherSprite4):
if currentBoss == 1:
if game.runtime() > lastBossFireTime + 1000:
info.change_life_by(-1)
camera_target.vx = cameraSpeed / 4
scene.camera_shake(4, 500)
pause(1000)
camera_target.vx = cameraSpeed
else:
info.change_life_by(-1)
pause(500)
sprites.on_overlap(SpriteKind.player, SpriteKind.Boss, on_on_overlap4)
projectile2: Sprite = None
darylDir = 0
chosenAction = 0
fireAngle = 0
isBossAttacking = False
lastFireTime = 0
isFacingLeft = False
camera_target: Sprite = None
projectile: Sprite = None
bossSpawn: tiles.Location = None
darylHorizontalSpeed = 0
currentFireIndex = 0
bossLocations: List[tiles.Location] = []
bossHealthThreshold = 0
lastBossFireTime = 0
cameraSpeed = 0
theBoss: Sprite = None
timeBetweenBossMove = 0
timeBetweenBossFire = 0
bossHealth = 0
bossSpeed = 0
statusbar: StatusBarSprite = None
currentBoss = 0
thePlayer: Sprite = None
jumpVelocity = 0
gravity = 0
tiles.set_tilemap(tilemap("""
level5
"""))
gravity = 300
jumpHeight = 34
playerSpeed = 100
projectileVelocity = 150
timeBetweenFire = 100
jumpVelocity = 0 - Math.sqrt(2 * (gravity * jumpHeight))
thePlayer = sprites.create(assets.image("""
player character
"""),
SpriteKind.player)
thePlayer.ay = gravity
controller.move_sprite(thePlayer, playerSpeed, 0)
info.set_life(10)
currentBoss = 0
statusbar = statusbars.create(100, 11, StatusBarKind.health)
statusbar.set_color(12, 1)
statusbar.set_bar_border(1, 3)
statusbar.right = 160
statusbar.top = 0
statusbar.set_flag(SpriteFlag.RELATIVE_TO_CAMERA, True)
createBoss(currentBoss)
def on_on_update():
global isFacingLeft, projectile, lastFireTime
if thePlayer.vx < 0:
isFacingLeft = True
elif thePlayer.vx > 0:
isFacingLeft = False
if controller.B.is_pressed():
if game.runtime() > lastFireTime + timeBetweenFire:
if controller.up.is_pressed():
projectile = sprites.create_projectile_from_sprite(assets.image("""
player projectile
"""),
thePlayer,
0,
0 - projectileVelocity)
elif isFacingLeft:
projectile = sprites.create_projectile_from_sprite(assets.image("""
player projectile
"""),
thePlayer,
0 - projectileVelocity,
0)
else:
projectile = sprites.create_projectile_from_sprite(assets.image("""
player projectile
"""),
thePlayer,
projectileVelocity,
0)
projectile.vx += thePlayer.vx
lastFireTime = game.runtime()
game.on_update(on_on_update)
def on_on_update2():
global projectile, lastBossFireTime, fireAngle, currentFireIndex
if currentBoss == 0:
if isBossAttacking:
if game.runtime() > lastBossFireTime + timeBetweenBossFire:
projectile = sprites.create_projectile_from_sprite(assets.image("""
albert projectile
"""),
theBoss,
-75,
0)
projectile.set_kind(SpriteKind.BossProjectile)
projectile.y += Math.random_range(-16, 16)
lastBossFireTime = game.runtime()
elif currentBoss == 1:
theBoss.left = scene.camera_left() + 12
theBoss.y = Math.map(Math.sin(game.runtime() / 1000),
-1,
1,
32,
scene.screen_height() - 32)
elif currentBoss == 2:
if game.runtime() > lastBossFireTime + timeBetweenBossFire:
animation.stop_animation(animation.AnimationTypes.ALL, theBoss)
theBoss.set_image(assets.image("""
chase
"""))
if bossHealth > bossHealthThreshold:
fireAngle = 0.7854 / 2 * currentFireIndex
projectile = sprites.create_projectile_from_sprite(assets.image("""
chase projectile
"""),
theBoss,
Math.cos(fireAngle) * projectileVelocity,
Math.sin(fireAngle) * projectileVelocity)
currentFireIndex += 1
projectile.set_kind(SpriteKind.BossProjectile)
else:
for index in range(8):
fireAngle = 0.7854 * index
projectile = sprites.create_projectile_from_sprite(assets.image("""
chase projectile
"""),
theBoss,
Math.cos(fireAngle) * projectileVelocity,
Math.sin(fireAngle) * projectileVelocity)
projectile.set_kind(SpriteKind.BossProjectile)
lastBossFireTime = game.runtime()
else:
pass
game.on_update(on_on_update2)
def on_on_update3():
if currentBoss == 1:
if thePlayer.right + 16 < scene.camera_left():
clearStage()
createBoss(currentBoss)
tiles.place_on_tile(thePlayer, tiles.get_tile_location(7, 4))
game.on_update(on_on_update3)
def on_forever():
global isBossAttacking, chosenAction, darylDir
if currentBoss == 0:
if theBoss.is_hitting_tile(CollisionDirection.BOTTOM) or theBoss.is_hitting_tile(CollisionDirection.TOP):
isBossAttacking = True
pause(timeBetweenBossMove)
isBossAttacking = False
if theBoss.is_hitting_tile(CollisionDirection.BOTTOM):
theBoss.vy = 0 - bossSpeed
else:
theBoss.vy = bossSpeed
pause(100)
elif currentBoss == 3:
if theBoss.is_hitting_tile(CollisionDirection.BOTTOM):
theBoss.vx = 0
chosenAction = Math.random_range(0, 2)
darylDir = 1
if thePlayer.x < theBoss.x:
darylDir = -1
if chosenAction == 0:
darylThrow(darylDir)
elif chosenAction == 1:
darylDash(darylDir)
else:
darylJump(darylDir)
forever(on_forever)
def on_update_interval():
global projectile2
if currentBoss == 1:
projectile2 = sprites.create_projectile_from_sprite(assets.image("""
bertha projectile
"""),
theBoss,
10,
0)
projectile2.set_kind(SpriteKind.BossProjectile)
projectile2.follow(thePlayer, 35)
sprites.set_data_number(projectile2, "health", 12)
game.on_update_interval(3000, on_update_interval)
Boss 冲刺游戏代码解读
这是一个使用MakeCode Arcade创建的Boss战游戏,包含多个不同类型的Boss战斗。
游戏概述
这是一个平台动作游戏,玩家需要与多个Boss进行战斗。每个Boss都有独特的行为模式和攻击方式。核心组件:
1. 自定义精灵类型
python
class SpriteKind:
Boss = SpriteKind.create()
BossProjectile = SpriteKind.create()
Camera = SpriteKind.create()
定义了三种特殊的精灵类型:Boss、Boss发射的子弹和相机目标。
2. 游戏初始化设置
设置重力、跳跃高度、玩家速度等物理参数
创建玩家角色并设置控制器
初始化生命值和状态栏
3. Boss系统
游戏包含多个Boss,每个都有独特的行为:
Boss 0 (Albert)
在屏幕上下移动
定期发射水平投射物
生命值降到一半时会加速
Boss 1 (Bertha)
跟随相机移动
周期性发射跟踪玩家的投射物
投射物可以被玩家射击摧毁
Boss 2 (Chase)
在预设位置之间传送
发射扇形投射物攻击
生命值降低后会发射全方位投射物
Boss 3 (Daryl)
平台跳跃型Boss
有三种攻击方式:投掷、冲刺和跳跃
根据玩家位置选择攻击方向
4. 玩家控制系统
左右移动控制
A键跳跃
B键发射子弹(可向上或水平发射)
5. 碰撞检测系统
玩家子弹与Boss的碰撞
Boss子弹与玩家的碰撞
玩家与Boss的直接碰撞
代码结构分析
1. Boss创建函数 createBoss()
根据当前Boss编号创建不同的Boss,设置各自的属性:
生命值
移动速度
攻击频率
特殊行为
2. Boss行为函数
每个Boss都有特定的行为函数:
initialize_camera(): 为Bertha Boss初始化相机跟随目标
initialize_chase(): 为Chase Boss初始化传送行为
darylThrow(), darylDash(), darylJump(): Daryl Boss的三种攻击方式
3. 更新循环
多个on_update函数处理不同方面的游戏逻辑:
玩家射击
Boss攻击模式
相机移动
Boss行为决策
4. 碰撞处理
多个on_overlap函数处理各种碰撞情况:
玩家子弹击中Boss
Boss子弹击中玩家
玩家与Boss碰撞
玩家子弹与Boss子弹碰撞
游戏特色
多样化的Boss战:每个Boss都有独特的战斗机制
状态栏系统:显示Boss生命值
动画效果:Boss和攻击都有相应的动画
渐进难度:Boss随着生命值降低会改变行为模式
屏幕震动效果:增强打击感。
图形编程参考实验程序
通过模拟器,调试与模拟运行


实验场景记录






评论