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

[项目] 【花雕动手做】基于 Kitronik 可编程开发板之粒子效果

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

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

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

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图3

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图1

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图2

驴友花雕  中级技神
 楼主|

发表于 3 小时前

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果

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

JavaScript 实验代码

  1. interface ParticleDemonstration {
  2.     start(): particles.ParticleSource[];
  3. }
  4. // show controls
  5. let ctlMessage = image.create(scene.screenWidth(), 10);
  6. ctlMessage.printCenter("Effects: 'A' (+), 'B' (-)", 0, 0);
  7. let msgSprite = sprites.create(ctlMessage);
  8. let msgInterval = 0;
  9. const myDemonstrations: ParticleDemonstration[] = [];
  10. let count = 1;
  11. controller.A.onEvent(ControllerButtonEvent.Pressed, function () {
  12.     count = Math.min(count + 1, 6);
  13. });
  14. controller.B.onEvent(ControllerButtonEvent.Pressed, function () {
  15.     count = Math.max(count - 1, 1);
  16. });
  17. namespace demonstrations {
  18.     export class Fire implements ParticleDemonstration {
  19.         start() {
  20.             const sources: particles.ParticleSource[] = [];
  21.             const factory = new particles.FireFactory(8);
  22.             const src = new particles.FireSource(makeSimpleAnchor(), 100, factory);
  23.             src.setAcceleration(0, -40);
  24.             sources.push(src);
  25.             return sources;
  26.         }
  27.     }
  28.     export class Spinner implements ParticleDemonstration {
  29.         start() {
  30.             class RingFactory extends particles.RadialFactory {
  31.                 createParticle(anchor: particles.ParticleAnchor) {
  32.                     const p = super.createParticle(anchor);
  33.                     p.lifespan = this.galois.randomRange(200, 350);
  34.                     return p;
  35.                 }
  36.             }
  37.             const sources: particles.ParticleSource[] = [];
  38.             const colors = [0x9, 0xA, 0xB, 0xC];
  39.             const factory = new RingFactory(20, 30, 10, colors);
  40.             const src = new particles.ParticleSource(makeSimpleAnchor(), 400, factory);
  41.             sources.push(src);
  42.             return sources;
  43.         }
  44.     }
  45.     export class BubbleConfetti implements ParticleDemonstration {
  46.         start() {
  47.             const sources: particles.ParticleSource[] = [];
  48.             const min = 1000;
  49.             const anchor = makeSimpleAnchor();
  50.             anchor.width = screen.width;
  51.             const bubbleFactory = new particles.BubbleFactory(anchor, min, min * 2.5);
  52.             sources.push(new particles.BubbleSource(anchor, 30, bubbleFactory.stateCount - 1, bubbleFactory));
  53.             const confettiFactory = new particles.ConfettiFactory(anchor.width, 16);
  54.             confettiFactory.setSpeed(50);
  55.             sources.push(new particles.ParticleSource(anchor, 50, confettiFactory));
  56.             return sources;
  57.         }
  58.     }
  59.     export class RadialGroup implements ParticleDemonstration {
  60.         start() {
  61.             const sources: particles.ParticleSource[] = [];
  62.             const anchor = makeSimpleAnchor();
  63.             const radius = Math.percentChance(50) ? 0 : 20;
  64.             const increaseRate = Math.percentChance(50);
  65.             control.runInParallel(() => {
  66.                 for (let i = 0; i < 3; ++i) {
  67.                     const colors = Math.percentChance(10) ?
  68.                         [0xC, 0xD, 0xE]
  69.                         :
  70.                         Math.percentChance(50) ?
  71.                             [0x6, 0x7, 0x8, 0x9, 0xA]
  72.                             :
  73.                             undefined;
  74.                     let factory: particles.ParticleFactory = new particles.RadialFactory(radius, 90, 5, colors);
  75.                     const src = new particles.ParticleSource(anchor, increaseRate ? 50 + (i * 50) : 100, factory);
  76.                     sources.push(src);
  77.                     pause(350);
  78.                 }
  79.             });
  80.             return sources;
  81.         }
  82.     }
  83.     export class Stars implements ParticleDemonstration {
  84.         start() {
  85.             class StarFactory extends particles.ParticleFactory {
  86.                 protected galois: Math.FastRandom;
  87.                 protected possibleColors: number[];
  88.                 images: Image[];
  89.                 constructor(possibleColors?: number[]) {
  90.                     super();
  91.                     this.galois = new Math.FastRandom();
  92.                     this.images = [
  93.                         img`
  94.                             1
  95.                         `, img`
  96.                             1 . 1
  97.                             . 1 .
  98.                             1 . 1
  99.                         `, img`
  100.                             . 1 .
  101.                             1 1 1
  102.                             . 1 .
  103.                         `
  104.                     ];
  105.                     if (possibleColors && possibleColors.length)
  106.                         this.possibleColors = possibleColors;
  107.                     else
  108.                         this.possibleColors = [1];
  109.                 }
  110.                 createParticle(anchor: particles.ParticleAnchor) {
  111.                     const p = super.createParticle(anchor);
  112.                     p._x = Fx8(this.galois.randomRange(0, screen.width));
  113.                     p._y = Fx8(0);
  114.                     p.vy = Fx8(this.galois.randomRange(40, 60));
  115.                     // set lifespan based off velocity and screen height (plus a little to make sure it doesn't disappear early)
  116.                     p.lifespan = Fx.toInt(Fx.mul(Fx.div(Fx8(screen.height + 20), p.vy), Fx8(1000)));
  117.                     const length = this.possibleColors.length - 1;
  118.                     p.color = this.possibleColors[this.possibleColors.length - 1];
  119.                     for (let i = 0; i < length; ++i) {
  120.                         if (this.galois.percentChance(50)) {
  121.                             p.color = this.possibleColors[i];
  122.                             break;
  123.                         }
  124.                     }
  125.                     // images besides the first one are only used on occasion
  126.                     p.data = this.galois.percentChance(15) ? this.galois.randomRange(1, this.images.length - 1) : 0;
  127.                     return p;
  128.                 }
  129.                 drawParticle(p: particles.Particle, x: Fx8, y: Fx8) {
  130.                     // on occasion, twinkle from white to yellow
  131.                     const twinkleFlag = 0x8000;
  132.                     const rest = 0x7FFF;
  133.                     if (twinkleFlag && p.data) {
  134.                         if (this.galois.percentChance(10)) {
  135.                             p.color = 1;
  136.                             p.data &= rest;
  137.                         }
  138.                     } else if (p.color === 1 && this.galois.percentChance(1)) {
  139.                         p.color = 5;
  140.                         p.data |= twinkleFlag;
  141.                     }
  142.                     const selected = this.images[rest & p.data].clone();
  143.                     selected.replace(0x1, p.color);
  144.                     screen.drawImage(selected, Fx.toInt(x), Fx.toInt(y));
  145.                 }
  146.             }
  147.             const sources: particles.ParticleSource[] = [];
  148.             const colors = [1];
  149.             for (let i = 0; i < 4; i++)
  150.                 colors.push(randint(2, 0xE));
  151.             const factory = new StarFactory(colors);
  152.             const src = new particles.ParticleSource(makeSimpleAnchor(), 25, factory)
  153.             sources.push(src);
  154.             return sources;
  155.         }
  156.     }
  157. }
  158. function makeSimpleAnchor(): particles.ParticleAnchor {
  159.     return {
  160.         x: screen.width >> 1,
  161.         y: screen.height >> 1
  162.     };
  163. }
  164. // Radial group as a lot of different possible configurations, so make it twice as likely as others
  165. for (let i = 0; i < 2; i++) {
  166.     myDemonstrations.push(new demonstrations.RadialGroup());
  167. }
  168. myDemonstrations.push(new demonstrations.Stars());
  169. myDemonstrations.push(new demonstrations.BubbleConfetti());
  170. myDemonstrations.push(new demonstrations.Spinner());
  171. myDemonstrations.push(new demonstrations.Fire());
  172. forever(() => {
  173.     particles.disableAll()
  174.     for (let i = 0; i < count; ++i) {
  175.         Math.pickRandom(myDemonstrations)
  176.             .start();
  177.     }
  178.     msgSprite.top = scene.screenHeight();
  179.     msgSprite.top += msgInterval % 5 == 0 ? -10 : 0;
  180.     msgInterval += 1;
  181.     pause(3000);
  182. });
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2 小时前

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果

这段 JavaScript 代码是基于 MakeCode Arcade 的粒子系统演示框架,展示了如何使用不同的粒子工厂(如火焰、星星、气泡、旋转环等)来创建丰富的视觉效果。它不仅体现了粒子系统的多样性,还通过控制器交互实现了动态切换和组合展示。逐段解读如下:

一、核心结构概览

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图1

二、控制器逻辑
ts
  1. controller.A.onEvent(...): count++
  2. controller.B.onEvent(...): count--
复制代码


按下 A 键:增加展示数量(最多 6)

按下 B 键:减少展示数量(最少 1)

三、粒子演示类详解
每个类都实现了 ParticleDemonstration 接口,返回一个或多个 ParticleSource。

1、Fire
使用 FireFactory 创建火焰效果

设置向上的加速度模拟火苗飘动

2、Spinner
使用 RadialFactory 创建旋转粒子环

自定义 lifespan,粒子存活时间随机

3、BubbleConfetti
同时生成气泡和五彩纸屑

使用两个不同的工厂:BubbleFactory 和 ConfettiFactory

4、RadialGroup
并行生成多个 RadialFactory 粒子源

每个源颜色、半径、速率都可能不同

使用 control.runInParallel() 异步创建,增强动态感

5、 Stars
自定义 StarFactory,生成闪烁星星

粒子图像有三种形态,颜色随机

实现 drawParticle() 方法,控制星星闪烁逻辑

四、辅助函数与配置
1、makeSimpleAnchor()
ts
  1. return { x: screen.width >> 1, y: screen.height >> 1 }
复制代码


返回一个屏幕中心点作为粒子源锚点

2、初始化演示列表
ts
  1. myDemonstrations.push(...)
  2. RadialGroup 被添加两次,增加其出现概率
复制代码



其余演示类各添加一次

五、主循环逻辑
ts
  1. forever(() => {
  2.     particles.disableAll()
  3.     for (let i = 0; i < count; ++i) {
  4.         Math.pickRandom(myDemonstrations).start();
  5.     }
  6.     ...
  7.     pause(3000);
  8. });
复制代码


每 3 秒:

清除所有粒子

随机选择 count 个演示类并启动

控制提示信息上下浮动,形成动态 UI 效果。



回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 2 小时前

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果

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

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图1

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图2

实验场景记录

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图4

【花雕动手做】基于 Kitronik 可编程开发板之粒子效果图3

回复

使用道具 举报

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

本版积分规则

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

硬件清单

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

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

mail