10浏览
查看: 10|回复: 4

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

[复制链接]
Kitronik ARCADE 是一款由英国教育科技公司 Kitronik 精心打造的可编程游戏机开发板,专为编程教学与创客实践而设计。该设备原生支持微软的 MakeCode Arcade 平台,用户可通过图形化或 JavaScript 编程方式,轻松创建、下载并运行复古风格的街机游戏。

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

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

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

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

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

驴友花雕  中级技神
 楼主|

发表于 昨天 21:37

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

作为学习、练习与尝试,这里创建一个方块 Boss 的小游戏。
打开网页版:https://arcade.makecode.com/,设置项目名称:方块 Boss

MicroPython实验参考代码

  1. @namespace
  2. class SpriteKind:
  3.     PlayerShot = SpriteKind.create()
  4.     LifeBar = SpriteKind.create()
  5. def moveSpriteInTime(sprite: Sprite, x: number, y: number, t: number):
  6.     global globalX, globalY, dx, dy
  7.     globalX = x
  8.     globalY = y
  9.     dx = x - sprite.x
  10.     dy = y - sprite.y
  11.     sprite.set_velocity(dx / t, dy / t)
  12. def on_on_overlap(sprite2, otherSprite):
  13.     global bossLife
  14.     if started:
  15.         info.change_score_by(20)
  16.         bossLife += -1
  17.         music.play_tone(208, music.beat(BeatFraction.EIGHTH))
  18.         lifeBarPic.fill_rect(bossLife * 2, 0, 96 - bossLife * 2, 5, 15)
  19.         lifeBar.set_image(lifeBarPic)
  20.         if bossLife <= 0:
  21.             game.over(True)
  22.         elif bossLife % 12 == 0:
  23.             preSetBossPosition(80, 30)
  24.     otherSprite.destroy()
  25. sprites.on_overlap(SpriteKind.enemy, SpriteKind.PlayerShot, on_on_overlap)
  26. def spell1():
  27.     enemyShootAimingPlayer(boss, 90, 5)
  28. def on_b_released():
  29.     controller.move_sprite(mySprite)
  30. controller.B.on_event(ControllerButtonEvent.RELEASED, on_b_released)
  31. def moveSpriteRandom(sprite3: Sprite, yLowerBound: number, outerBound: number, v: number):
  32.     moveSprite(sprite3,
  33.         randint(outerBound, scene.screen_width() - outerBound),
  34.         randint(outerBound, yLowerBound),
  35.         v)
  36. def on_a_pressed():
  37.     shootBulletFromSprite(mySprite, 200, -90)
  38. controller.A.on_event(ControllerButtonEvent.PRESSED, on_a_pressed)
  39. def nonSpell1():
  40.     global offset
  41.     index2 = 0
  42.     while index2 <= MAX - 1:
  43.         shootBulletFromSprite(boss, 60, 360 / MAX * index2 + offset)
  44.         index2 += 1
  45.     offset += 13
  46. def spell2():
  47.     global offset
  48.     for index in range(5):
  49.         shootBulletFromSprite(boss, 60, offset + index * 30)
  50.     offset += 23
  51. def on_on_overlap2(sprite4, otherSprite2):
  52.     info.change_life_by(-1)
  53.     scene.camera_shake(3, 200)
  54.     music.play_tone(139, music.beat(BeatFraction.EIGHTH))
  55.     otherSprite2.destroy()
  56. sprites.on_overlap(SpriteKind.player, SpriteKind.projectile, on_on_overlap2)
  57. def on_b_pressed():
  58.     controller.move_sprite(mySprite, 50, 50)
  59. controller.B.on_event(ControllerButtonEvent.PRESSED, on_b_pressed)
  60. def preSetBossPosition(x2: number, y2: number):
  61.     global started, ready, offset
  62.     started = False
  63.     ready = False
  64.     offset = 0
  65.     moveSpriteInTime(boss, x2, y2, 1)
  66. def moveSpriteRandomFixedTime(sprite5: Sprite, yLowerBound2: number, outerBound2: number, u: number):
  67.     moveSpriteInTime(sprite5,
  68.         randint(outerBound2, scene.screen_width() - outerBound2),
  69.         randint(outerBound2, yLowerBound2),
  70.         u)
  71. def nonSpell2():
  72.     index3 = 0
  73.     while index3 <= MAX - 1:
  74.         shootBulletFromSprite(boss, 60, 360 / MAX * index3 + offset)
  75.         shootBulletFromSprite(boss, 100, 360 / MAX * (index3 + 0.5) + offset)
  76.         index3 += 1
  77. def shootBulletFromSprite(sourceSprite: Sprite, speed: number, angle: number):
  78.     global projectile
  79.     projectile = sprites.create_projectile_from_sprite(img("""
  80.             . . . . . . . . . . . . . . . .
  81.             . . . . . . . . . . . . . . . .
  82.             . . . . . . . . . . . . . . . .
  83.             . . . . . . . . . . . . . . . .
  84.             . . . . . . . . . . . . . . . .
  85.             . . . . . . . . . . . . . . . .
  86.             . . . . . . . . . . . . . . . .
  87.             . . . . . . . . . . . . . . . .
  88.             . . . . . . . . . . . . . . . .
  89.             . . . . . . . . . . . . . . . .
  90.             . . . . . . . . . . . . . . . .
  91.             . . . . . . . . . . . . . . . .
  92.             . . . . . . . . . . . . . . . .
  93.             . . . . . . . . . . . . . . . .
  94.             . . . . . . . . . . . . . . . .
  95.             . . . . . . . . . . . . . . . .
  96.             """),
  97.         sourceSprite,
  98.         speed * Math.cos(angle / 57.3),
  99.         speed * Math.sin(angle / 57.3))
  100.     projectile.set_flag(SpriteFlag.AUTO_DESTROY, True)
  101.     if sourceSprite.kind() == SpriteKind.player:
  102.         projectile.set_kind(SpriteKind.PlayerShot)
  103.         projectile.set_image(img("""
  104.             . . . . . . . . . . . . . . . .
  105.             . . . . . . . . . . . . . . . .
  106.             . . . . . . . . . . . . . . . .
  107.             . . . . . . . . . . . . . . . .
  108.             . . . . . . . . . . . . . . . .
  109.             . . . . . . . 5 5 . . . . . . .
  110.             . . . . . . 5 4 4 5 . . . . . .
  111.             . . . . . 5 4 2 2 4 5 . . . . .
  112.             . . . . . 5 4 2 2 4 5 . . . . .
  113.             . . . . . . 5 4 4 5 . . . . . .
  114.             . . . . . . . 5 5 . . . . . . .
  115.             . . . . . . . . . . . . . . . .
  116.             . . . . . . . . . . . . . . . .
  117.             . . . . . . . . . . . . . . . .
  118.             . . . . . . . . . . . . . . . .
  119.             . . . . . . . . . . . . . . . .
  120.             """))
  121.     else:
  122.         projectile.set_image(img("""
  123.             . . . . . . . . . . . . . . . .
  124.             . . . . . . . . . . . . . . . .
  125.             . . . . . . . . . . . . . . . .
  126.             . . . . . . . . . . . . . . . .
  127.             . . . . . . . . . . . . . . . .
  128.             . . . . . . . 9 9 . . . . . . .
  129.             . . . . . . 9 6 6 9 . . . . . .
  130.             . . . . . 9 6 8 8 6 9 . . . . .
  131.             . . . . . 9 6 8 8 6 9 . . . . .
  132.             . . . . . . 9 6 6 9 . . . . . .
  133.             . . . . . . . 9 9 . . . . . . .
  134.             . . . . . . . . . . . . . . . .
  135.             . . . . . . . . . . . . . . . .
  136.             . . . . . . . . . . . . . . . .
  137.             . . . . . . . . . . . . . . . .
  138.             . . . . . . . . . . . . . . . .
  139.             """))
  140. def moveSprite(sprite6: Sprite, x3: number, y3: number, w: number):
  141.     global globalX, globalY, dx, dy, speed3
  142.     globalX = x3
  143.     globalY = y3
  144.     dx = x3 - sprite6.x
  145.     dy = y3 - sprite6.y
  146.     speed3 = Math.sqrt(dx * dx + dy * dy)
  147.     if speed3 != 0:
  148.         sprite6.set_velocity(dx / speed3 * w, dy / speed3 * w)
  149. def enemyShootAimingPlayer(sprite7: Sprite, speed2: number, spread: number):
  150.     shootBulletFromSprite(sprite7,
  151.         speed2,
  152.         Math.atan2(mySprite.y - sprite7.y, mySprite.x - sprite7.x) * 57.3 + randint(0 - spread, spread))
  153. lifeBarProgress = 0
  154. bossProgress = 0
  155. speed3 = 0
  156. projectile: Sprite = None
  157. ready = False
  158. started = False
  159. dy = 0
  160. dx = 0
  161. globalY = 0
  162. globalX = 0
  163. MAX = 0
  164. offset = 0
  165. lifeBar: Sprite = None
  166. lifeBarPic: Image = None
  167. boss: Sprite = None
  168. mySprite: Sprite = None
  169. bossLife = 0
  170. bossLife = 48
  171. info.set_life(20)
  172. info.set_score(0)
  173. music.set_volume(20)
  174. mySprite = sprites.create(img("""
  175.         . . . . . . f f f f . . . . . .
  176.         . . . . f f e e e e f f . . . .
  177.         . . . f e e e f f e e e f . . .
  178.         . . f f f f f 2 2 f f f f f . .
  179.         . . f f e 2 e 2 2 e 2 e f f . .
  180.         . . f e 2 f 2 f f 2 f 2 e f . .
  181.         . . f f f 2 2 e e 2 2 f f f . .
  182.         . f f e f 2 f e e f 2 f e f f .
  183.         . f e e f f e e e e f e e e f .
  184.         . . f e e e e e e e e e e f . .
  185.         . . . f e e e e e e e e f . . .
  186.         . . e 4 f f f f f f f f 4 e . .
  187.         . . 4 d f 2 2 2 2 2 2 f d 4 . .
  188.         . . 4 4 f 4 4 4 4 4 4 f 4 4 . .
  189.         . . . . . f f f f f f . . . . .
  190.         . . . . . f f . . f f . . . . .
  191.         """),
  192.     SpriteKind.player)
  193. mySprite.set_position(80, 105)
  194. mySprite.set_flag(SpriteFlag.STAY_IN_SCREEN, True)
  195. controller.move_sprite(mySprite)
  196. boss = sprites.create(img("""
  197.         ........................
  198.         ........................
  199.         ........................
  200.         ........................
  201.         ..........ffff..........
  202.         ........ff1111ff........
  203.         .......fb111111bf.......
  204.         .......f11111111f.......
  205.         ......fd11111111df......
  206.         ......fd11111111df......
  207.         ......fddd1111dddf......
  208.         ......fbdbfddfbdbf......
  209.         ......fcdcf11fcdcf......
  210.         .......fb111111bf.......
  211.         ......fffcdb1bdffff.....
  212.         ....fc111cbfbfc111cf....
  213.         ....f1b1b1ffff1b1b1f....
  214.         ....fbfbffffffbfbfbf....
  215.         .........ffffff.........
  216.         ...........fff..........
  217.         ........................
  218.         ........................
  219.         ........................
  220.         ........................
  221.         """),
  222.     SpriteKind.enemy)
  223. boss.set_position(-16, -16)
  224. lifeBarPic = image.create(96, 5)
  225. lifeBar = sprites.create(lifeBarPic, SpriteKind.LifeBar)
  226. lifeBar.set_position(80, 5)
  227. lifeBar.set_flag(SpriteFlag.GHOST, True)
  228. offset = 0
  229. MAX = 10
  230. bossCanMove = True
  231. preSetBossPosition(80, 30)
  232. def on_on_update():
  233.     global bossProgress, bossCanMove, MAX, ready
  234.     if abs(boss.x - globalX) + abs(boss.y - globalY) <= 2:
  235.         boss.set_velocity(0, 0)
  236.         if not (ready):
  237.             bossProgress += 1
  238.             if bossProgress == 2:
  239.                 bossCanMove = False
  240.             else:
  241.                 if bossProgress == 2:
  242.                     MAX = 8
  243.                 bossCanMove = True
  244.         ready = True
  245. game.on_update(on_on_update)
  246. def on_update_interval():
  247.     if started:
  248.         if bossProgress == 3:
  249.             nonSpell2()
  250. game.on_update_interval(750, on_update_interval)
  251. def on_update_interval2():
  252.     if started and bossCanMove:
  253.         moveSpriteRandom(boss, 40, 8, 60)
  254. game.on_update_interval(2500, on_update_interval2)
  255. def on_update_interval3():
  256.     if started:
  257.         if bossProgress == 2:
  258.             spell1()
  259.         else:
  260.             if bossProgress == 4:
  261.                 spell2()
  262. game.on_update_interval(150, on_update_interval3)
  263. def on_update_interval4():
  264.     if started:
  265.         if bossProgress == 1:
  266.             nonSpell1()
  267. game.on_update_interval(500, on_update_interval4)
  268. def on_update_interval5():
  269.     global lifeBarProgress, started
  270.     if ready and not (started):
  271.         if lifeBarProgress < 4:
  272.             lifeBarPic.fill_rect(24 * lifeBarProgress, 0, 24, 5, 14 - lifeBarProgress % 2 * 6)
  273.             lifeBarPic.fill_rect(24 * lifeBarProgress, 1, 24, 3, lifeBarProgress % 2 * 5 + 4)
  274.             lifeBar.set_image(lifeBarPic)
  275.             lifeBarProgress += 1
  276.         else:
  277.             started = True
  278. game.on_update_interval(100, on_update_interval5)
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 昨天 21:42

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

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

一、游戏核心机制概览

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


二、关键逻辑模块解析
1、玩家与 Boss 初始化
python
  1. mySprite = sprites.create(..., SpriteKind.player)
  2. boss = sprites.create(..., SpriteKind.enemy)
复制代码


玩家角色使用 controller.move_sprite() 控制移动

Boss 初始位置为屏幕外,随后通过 preSetBossPosition() 移入战场

2、Boss 生命条加载动画
python
  1. on_update_interval5()
复制代码


每 100ms 更新一次生命条图像,逐步填充

加载完成后 started = True,正式进入战斗状态

3、玩家攻击 Boss
python
  1. controller.A.on_event(...): shootBulletFromSprite(mySprite, 200, -90)
  2. sprites.on_overlap(SpriteKind.enemy, SpriteKind.PlayerShot, on_on_overlap)
复制代码


玩家按 A 键发射子弹

子弹击中 Boss 后:

得分 +20

Boss 生命值减少

更新生命条图像

每损失 12 点生命触发一次位置重置

4、Boss 攻击模式切换
python
  1. bossProgress = 1 → nonSpell1()
  2. bossProgress = 2 → spell1()
  3. bossProgress = 3 → nonSpell2()
  4. bossProgress = 4 → spell2()
复制代码


每种攻击模式通过定时器触发不同弹幕函数

nonSpell1():环形弹幕

spell1():追踪弹

nonSpell2():双层环形弹幕

spell2():扇形弹幕

5、Boss 移动逻辑
python
  1. moveSpriteRandom(boss, 40, 8, 60)
复制代码


每 2.5 秒随机移动一次

使用 moveSprite() 或 moveSpriteInTime() 实现平滑移动

到达目标位置后触发阶段推进逻辑

6、玩家受伤逻辑
python
  1. sprites.on_overlap(SpriteKind.player, SpriteKind.projectile, on_on_overlap2)
复制代码


玩家被 Boss 弹幕击中:

生命值 -1

屏幕震动

播放音效

三、弹幕生成机制
shootBulletFromSprite(source, speed, angle)
使用三角函数计算速度向量

根据发射者类型设置不同图像与种类

自动销毁,避免资源堆积

enemyShootAimingPlayer(sprite, speed, spread)
计算玩家与 Boss 的角度

添加随机偏移实现“散射追踪弹”。


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 昨天 21:44

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

图形编程参考实验程序


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

回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 昨天 21:46

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

通过模拟器,调试与模拟运行

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

实验场景记录

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

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

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail