# 九艾斯 3D 打飞机 · 原生 9S + 4D 虚拟机 # 绵阳九艾斯科技有限公司 版权所有 (c) 2026 # 运行: 九S虚拟机(4D).exe 打飞机3D.9s 常量 颜色_白 = 16777215 常量 颜色_红 = 255 常量 颜色_绿 = 65280 常量 颜色_蓝 = 16711680 常量 颜色_黄 = 65535 常量 颜色_青 = 16776960 常量 颜色_紫 = 16711935 常量 颜色_橙 = 42495 常量 颜色_深灰 = 8421504 常量 颜色_黑 = 0 常量 地面颜色 = 2105376 常量 颜色_暗蓝 = 4202528 # ===================================================================== # ★★★ 可改配置:只改下面这几行,重新运行就能看到不同 3D 效果 ★★★ # ===================================================================== 变量 玩家颜色 = 颜色_青 # 战机颜色:颜色_青 / 颜色_绿 / 颜色_蓝 / 颜色_黄 / 颜色_白 变量 子弹颜色 = 颜色_黄 # 子弹颜色 变量 敌机颜色 = 颜色_红 # 敌机颜色 变量 爆炸颜色 = 颜色_橙 # 爆炸粒子颜色 变量 星空颜色 = 颜色_白 # 星空颜色 变量 玩家速度 = 24 # 每按一次方向键移动的 3D 距离(越大越灵活) 变量 子弹速度 = 38 # 子弹每帧前进的 3D 距离(越大越快) 变量 敌机基础速度 = 14 # 敌机每帧接近速度(越大越难) 变量 开火间隔 = 8 # 两次开火间隔多少帧(越小射得越快) 变量 生成间隔 = 18 # 每隔多少帧生成一架敌机(越小敌机越多) 变量 爆炸粒子数 = 8 # 每次爆炸生成多少 3D 粒子 变量 相机跟随 = 40 # 相机横向跟随百分比(0=固定,100=完全跟随) 变量 相机距离 = 900 # 相机离战机的距离(越大越远) 变量 特效开关 = 真 # 真=3D 粒子爆炸;假=关闭爆炸 变量 初始生命 = 5 # 开局生命数(撞机扣 1 条) 变量 自动开火 = 真 # 真=自动连续开火;假=只按空格开火 # ===================================================================== 变量 窗口 = 空 变量 场景 = 0 变量 机身ID = 0 变量 左翼ID = 0 变量 右翼ID = 0 变量 座舱ID = 0 变量 引擎左ID = 0 变量 引擎右ID = 0 变量 玩家X = 0 变量 玩家Y = 40 变量 玩家Z = -260 变量 星星ID = [] 变量 星星X = [] 变量 星星Y = [] 变量 星星Z = [] 变量 星星速度 = [] 变量 敌机ID = [] 变量 敌机X = [] 变量 敌机Y = [] 变量 敌机Z = [] 变量 敌机速度 = [] 变量 子弹ID = [] 变量 子弹X = [] 变量 子弹Y = [] 变量 子弹Z = [] 变量 粒子ID = [] 变量 粒子X = [] 变量 粒子Y = [] 变量 粒子Z = [] 变量 粒子VX = [] 变量 粒子VY = [] 变量 粒子VZ = [] 变量 粒子寿命 = [] 变量 分数 = 0 变量 生命 = 3 变量 游戏结束 = 假 变量 计时 = 0 变量 上次开火 = -100 # 清空所有列表 函数 清空列表() { 敌机ID = [] 敌机X = [] 敌机Y = [] 敌机Z = [] 敌机速度 = [] 子弹ID = [] 子弹X = [] 子弹Y = [] 子弹Z = [] 粒子ID = [] 粒子X = [] 粒子Y = [] 粒子Z = [] 粒子VX = [] 粒子VY = [] 粒子VZ = [] 粒子寿命 = [] } # 删除场景里还活着的动态物体 函数 清理动态物体() { 变量 序号 = 列表长度(敌机ID) - 1 当 (序号 >= 0) { 删除3D物体(场景, 列表查询(敌机ID, 序号)) 序号 = 序号 - 1 } 序号 = 列表长度(子弹ID) - 1 当 (序号 >= 0) { 删除3D物体(场景, 列表查询(子弹ID, 序号)) 序号 = 序号 - 1 } 序号 = 列表长度(粒子ID) - 1 当 (序号 >= 0) { 删除3D物体(场景, 列表查询(粒子ID, 序号)) 序号 = 序号 - 1 } } # 创建一次 3D 场景:地面、网格、战机、星空 函数 创建场景() { 如果 (场景 != 0) { 返回 } 场景 = 创建3D场景(1400) 设置3D相机(场景, 0, 70, 0 - 相机距离) 设置相机注视(场景, 0, 20, 300) 设置3D光源(场景, 0.4, -0.8, -0.3, 0.75) 设置泛光(0) 设置环境光遮蔽(0) 设置色彩校正(1, 1.2, 1.08, -0.02) 设置画质档位(0) 设置渲染质量(2) # 科技网格(少量线条,保 3D 纵深又不过度耗性能) 变量 格X = -600 当 (格X <= 600) { 变量 格ID = 添加立方体(场景, 格X, -150, 300, 3, 2, 1900, 颜色_暗蓝) 设置塑料材质(场景, 格ID, 颜色_暗蓝) 设置3D物体材质(场景, 格ID, 颜色_暗蓝, 0.0, 8, 1.0, 0.0, 0.3, 0.25) 格X = 格X + 300 } 变量 格Z = -400 当 (格Z <= 1000) { 变量 横ID = 添加立方体(场景, 0, -150, 格Z, 1300, 2, 3, 颜色_暗蓝) 设置塑料材质(场景, 横ID, 颜色_暗蓝) 设置3D物体材质(场景, 横ID, 颜色_暗蓝, 0.0, 8, 1.0, 0.0, 0.3, 0.25) 格Z = 格Z + 280 } # 3D 战机:机身 + 左右翼 + 座舱 + 双引擎光点(金属/自发光材质) 机身ID = 添加立方体(场景, 0, 10, -280, 46, 18, 76, 玩家颜色) 左翼ID = 添加立方体(场景, -40, 8, -270, 56, 8, 38, 玩家颜色) 右翼ID = 添加立方体(场景, 40, 8, -270, 56, 8, 38, 玩家颜色) 座舱ID = 添加立方体(场景, 0, 22, -248, 20, 12, 16, 颜色_白) 引擎左ID = 添加粒子(场景, -16, 10, -238, 8, 颜色_青) 引擎右ID = 添加粒子(场景, 16, 10, -238, 8, 颜色_青) 设置金属材质(场景, 机身ID, 玩家颜色) 设置金属材质(场景, 左翼ID, 玩家颜色) 设置金属材质(场景, 右翼ID, 玩家颜色) 设置气泡材质(场景, 座舱ID, 颜色_白) 设置3D物体材质(场景, 座舱ID, 颜色_白, 0.9, 32, 0.85, 0.2, 0.4, 0.4) 设置气泡材质(场景, 引擎左ID, 颜色_青) 设置气泡材质(场景, 引擎右ID, 颜色_青) 设置3D物体材质(场景, 引擎左ID, 颜色_青, 0.0, 8, 1.0, 0.0, 0.2, 0.95) 设置3D物体材质(场景, 引擎右ID, 颜色_青, 0.0, 8, 1.0, 0.0, 0.2, 0.95) # 星空:16 颗 3D 星点(自发光) 变量 序号 = 0 当 (序号 < 16) { 变量 星X = 随机整数(-700, 700) 变量 星Y = 随机整数(-120, 300) 变量 星Z = 随机整数(-400, 1100) 变量 星ID = 添加立方体(场景, 星X, 星Y, 星Z, 4, 4, 4, 星空颜色) 设置气泡材质(场景, 星ID, 星空颜色) 设置3D物体材质(场景, 星ID, 星空颜色, 0.0, 8, 1.0, 0.0, 0.2, 0.9) 列表追加(星星ID, 星ID) 列表追加(星星X, 星X) 列表追加(星星Y, 星Y) 列表追加(星星Z, 星Z) 列表追加(星星速度, 随机整数(8, 36)) 序号 = 序号 + 1 } } # 初始化 / R 键重开 函数 初始化() { 清理动态物体() 清空列表() 分数 = 0 生命 = 初始生命 游戏结束 = 假 计时 = 0 上次开火 = -100 玩家X = 0 玩家Y = 40 创建场景() 移动战机() 播放音效文件("资源/start.wav") } # 把战机四个部件移动到当前坐标 函数 移动战机() { 移动3D物体(场景, 机身ID, 玩家X, 玩家Y, 玩家Z) 移动3D物体(场景, 左翼ID, 玩家X - 40, 玩家Y - 2, 玩家Z + 10) 移动3D物体(场景, 右翼ID, 玩家X + 40, 玩家Y - 2, 玩家Z + 10) 移动3D物体(场景, 座舱ID, 玩家X, 玩家Y + 12, 玩家Z + 32) 移动3D物体(场景, 引擎左ID, 玩家X - 16, 玩家Y, 玩家Z + 42) 移动3D物体(场景, 引擎右ID, 玩家X + 16, 玩家Y, 玩家Z + 42) } # 发射一颗 3D 子弹 函数 发射() { 如果 (列表长度(子弹ID) >= 18) { 返回 } 变量 弹ID = 添加立方体(场景, 玩家X, 玩家Y, 玩家Z + 55, 14, 14, 52, 子弹颜色) 播放音效文件("资源/harddrop.wav") 设置气泡材质(场景, 弹ID, 子弹颜色) 设置3D物体材质(场景, 弹ID, 子弹颜色, 0.0, 8, 1.0, 0.1, 0.3, 0.9) 列表追加(子弹ID, 弹ID) 列表追加(子弹X, 玩家X) 列表追加(子弹Y, 玩家Y) 列表追加(子弹Z, 玩家Z + 55) } # 更新子弹,飞出场景的删除 函数 更新子弹() { 变量 新ID = [] 变量 新X = [] 变量 新Y = [] 变量 新Z = [] 变量 序号 = 0 当 (序号 < 列表长度(子弹ID)) { 变量 弹Z = 列表查询(子弹Z, 序号) + 子弹速度 如果 (弹Z < 1150) { 移动3D物体(场景, 列表查询(子弹ID, 序号), 列表查询(子弹X, 序号), 列表查询(子弹Y, 序号), 弹Z) 列表追加(新ID, 列表查询(子弹ID, 序号)) 列表追加(新X, 列表查询(子弹X, 序号)) 列表追加(新Y, 列表查询(子弹Y, 序号)) 列表追加(新Z, 弹Z) } 否则 { 删除3D物体(场景, 列表查询(子弹ID, 序号)) } 序号 = 序号 + 1 } 子弹ID = 新ID 子弹X = 新X 子弹Y = 新Y 子弹Z = 新Z } # 按间隔生成敌机 函数 生成敌机() { 如果 (计时 % 生成间隔 != 0) { 返回 } 如果 (列表长度(敌机ID) >= 5) { 返回 } 变量 机X = 玩家X + 随机整数(-200, 200) 如果 (机X < -340) { 机X = -340 } 如果 (机X > 340) { 机X = 340 } 变量 机Y = 玩家Y + 随机整数(-50, 60) 如果 (机Y < -40) { 机Y = -40 } 如果 (机Y > 170) { 机Y = 170 } 变量 机ID = 添加立方体(场景, 机X, 机Y, 1000, 64, 38, 46, 敌机颜色) 设置气泡材质(场景, 机ID, 敌机颜色) 设置3D物体材质(场景, 机ID, 敌机颜色, 0.0, 8, 1.0, 0.1, 0.3, 0.85) 变量 速度 = 敌机基础速度 + 取整(分数 / 200) 如果 (速度 > 30) { 速度 = 30 } 列表追加(敌机ID, 机ID) 列表追加(敌机X, 机X) 列表追加(敌机Y, 机Y) 列表追加(敌机Z, 1000) 列表追加(敌机速度, 速度) } # 敌机向玩家方向接近,穿过玩家的删除 函数 更新敌机() { 变量 新ID = [] 变量 新X = [] 变量 新Y = [] 变量 新Z = [] 变量 新速 = [] 变量 序号 = 0 当 (序号 < 列表长度(敌机ID)) { 变量 机Z = 列表查询(敌机Z, 序号) - 列表查询(敌机速度, 序号) 如果 (机Z > -360) { 移动3D物体(场景, 列表查询(敌机ID, 序号), 列表查询(敌机X, 序号), 列表查询(敌机Y, 序号), 机Z) 列表追加(新ID, 列表查询(敌机ID, 序号)) 列表追加(新X, 列表查询(敌机X, 序号)) 列表追加(新Y, 列表查询(敌机Y, 序号)) 列表追加(新Z, 机Z) 列表追加(新速, 列表查询(敌机速度, 序号)) } 否则 { 删除3D物体(场景, 列表查询(敌机ID, 序号)) } 序号 = 序号 + 1 } 敌机ID = 新ID 敌机X = 新X 敌机Y = 新Y 敌机Z = 新Z 敌机速度 = 新速 } # 3D 爆炸粒子 函数 爆炸(中心X, 中心Y, 中心Z) { 如果 (特效开关 == 假) { 返回 } 播放音效文件("资源/clear.wav") 变量 序号 = 0 当 (序号 < 爆炸粒子数) { 变量 粒X = 中心X + 随机整数(-16, 16) 变量 粒Y = 中心Y + 随机整数(-16, 16) 变量 粒Z = 中心Z + 随机整数(-16, 16) 变量 粒ID = 添加粒子(场景, 粒X, 粒Y, 粒Z, 9, 爆炸颜色) 设置气泡材质(场景, 粒ID, 爆炸颜色) 设置3D物体材质(场景, 粒ID, 爆炸颜色, 0.0, 8, 0.9, 0.1, 0.3, 0.95) 列表追加(粒子ID, 粒ID) 列表追加(粒子X, 粒X) 列表追加(粒子Y, 粒Y) 列表追加(粒子Z, 粒Z) 列表追加(粒子VX, 随机整数(-14, 14)) 列表追加(粒子VY, 随机整数(-6, 18)) 列表追加(粒子VZ, 随机整数(-10, 10)) 列表追加(粒子寿命, 18) 序号 = 序号 + 1 } } # 粒子飞散并逐渐消失 函数 更新粒子() { 变量 新ID = [] 变量 新X = [] 变量 新Y = [] 变量 新Z = [] 变量 新VX = [] 变量 新VY = [] 变量 新VZ = [] 变量 新寿命 = [] 变量 序号 = 0 当 (序号 < 列表长度(粒子ID)) { 变量 余命 = 列表查询(粒子寿命, 序号) - 1 如果 (余命 > 0) { 变量 粒X = 列表查询(粒子X, 序号) + 列表查询(粒子VX, 序号) 变量 粒Y = 列表查询(粒子Y, 序号) + 列表查询(粒子VY, 序号) 变量 粒Z = 列表查询(粒子Z, 序号) + 列表查询(粒子VZ, 序号) 变量 粒VY = 列表查询(粒子VY, 序号) - 1 移动3D物体(场景, 列表查询(粒子ID, 序号), 粒X, 粒Y, 粒Z) 列表追加(新ID, 列表查询(粒子ID, 序号)) 列表追加(新X, 粒X) 列表追加(新Y, 粒Y) 列表追加(新Z, 粒Z) 列表追加(新VX, 列表查询(粒子VX, 序号)) 列表追加(新VY, 粒VY) 列表追加(新VZ, 列表查询(粒子VZ, 序号)) 列表追加(新寿命, 余命) } 否则 { 删除3D物体(场景, 列表查询(粒子ID, 序号)) } 序号 = 序号 + 1 } 粒子ID = 新ID 粒子X = 新X 粒子Y = 新Y 粒子Z = 新Z 粒子VX = 新VX 粒子VY = 新VY 粒子VZ = 新VZ 粒子寿命 = 新寿命 } # 3D 碰撞:子弹打敌机、敌机撞玩家 函数 检测碰撞() { 变量 弹序 = 列表长度(子弹ID) - 1 当 (弹序 >= 0) { 变量 机序 = 列表长度(敌机ID) - 1 变量 已击中 = 假 当 (机序 >= 0 且 已击中 == 假) { 变量 差X = 列表查询(子弹X, 弹序) - 列表查询(敌机X, 机序) 变量 差Y = 列表查询(子弹Y, 弹序) - 列表查询(敌机Y, 机序) 变量 差Z = 列表查询(子弹Z, 弹序) - 列表查询(敌机Z, 机序) 如果 (差X > -50 且 差X < 50 且 差Y > -60 且 差Y < 60 且 差Z > -100 且 差Z < 100) { 爆炸(列表查询(敌机X, 机序), 列表查询(敌机Y, 机序), 列表查询(敌机Z, 机序)) 删除3D物体(场景, 列表查询(子弹ID, 弹序)) 删除3D物体(场景, 列表查询(敌机ID, 机序)) 列表移除(子弹ID, 弹序) 列表移除(子弹X, 弹序) 列表移除(子弹Y, 弹序) 列表移除(子弹Z, 弹序) 列表移除(敌机ID, 机序) 列表移除(敌机X, 机序) 列表移除(敌机Y, 机序) 列表移除(敌机Z, 机序) 列表移除(敌机速度, 机序) 分数 = 分数 + 10 已击中 = 真 } 机序 = 机序 - 1 } 弹序 = 弹序 - 1 } 变量 机序2 = 列表长度(敌机ID) - 1 当 (机序2 >= 0) { 变量 差X2 = 玩家X - 列表查询(敌机X, 机序2) 变量 差Y2 = 玩家Y - 列表查询(敌机Y, 机序2) 变量 差Z2 = 玩家Z - 列表查询(敌机Z, 机序2) 如果 (差X2 > -40 且 差X2 < 40 且 差Y2 > -34 且 差Y2 < 34 且 差Z2 > -60 且 差Z2 < 60) { 爆炸(列表查询(敌机X, 机序2), 列表查询(敌机Y, 机序2), 列表查询(敌机Z, 机序2)) 删除3D物体(场景, 列表查询(敌机ID, 机序2)) 列表移除(敌机ID, 机序2) 列表移除(敌机X, 机序2) 列表移除(敌机Y, 机序2) 列表移除(敌机Z, 机序2) 列表移除(敌机速度, 机序2) 生命 = 生命 - 1 播放音效文件("资源/drop.wav") 如果 (生命 <= 0) { 游戏结束 = 真 播放音效文件("资源/gameover.wav") } } 机序2 = 机序2 - 1 } } # 星空向相机飞来,越界后回到远处随机重生 函数 更新星空() { 变量 新ID = [] 变量 新X = [] 变量 新Y = [] 变量 新Z = [] 变量 新速 = [] 变量 序号 = 0 当 (序号 < 列表长度(星星ID)) { 变量 星X = 列表查询(星星X, 序号) 变量 星Y = 列表查询(星星Y, 序号) 变量 星Z = 列表查询(星星Z, 序号) - 列表查询(星星速度, 序号) 变量 速度 = 列表查询(星星速度, 序号) 如果 (星Z < -430) { 星Z = 1100 星X = 随机整数(-700, 700) 星Y = 随机整数(-120, 300) 速度 = 随机整数(8, 36) } 移动3D物体(场景, 列表查询(星星ID, 序号), 星X, 星Y, 星Z) 列表追加(新ID, 列表查询(星星ID, 序号)) 列表追加(新X, 星X) 列表追加(新Y, 星Y) 列表追加(新Z, 星Z) 列表追加(新速, 速度) 序号 = 序号 + 1 } 星星ID = 新ID 星星X = 新X 星星Y = 新Y 星星Z = 新Z 星星速度 = 新速 } # 相机跟随:横向轻微跟随,注视前方 函数 更新相机() { 变量 跟随X = 玩家X * 相机跟随 / 100 变量 摆动 = 正弦(计时 * 0.03) * 12 相机运镜(场景, 跟随X + 摆动, 70, 0 - 相机距离, 跟随X * 0.4, 20, 300) } # 绘制 HUD 函数 绘制界面() { 画布_清屏(九S画布()) 渲染3D场景(场景) 画布_画文本(九S画布(), 20, 18, "分数: " + 文本(分数), 颜色_白, 22) 画布_画文本(九S画布(), 20, 50, "生命: " + 文本(生命), 颜色_黄, 20) 画布_画文本(九S画布(), 20, 80, "3D 原生 9S · 4D 虚拟机", 颜色_青, 16) 画布_画文本(九S画布(), 20, 108, "帧率: " + 文本(取整(获取渲染帧率())) + " fps", 颜色_绿, 14) 画布_画文本(九S画布(), 20, 560, "方向键 3D 移动 空格 开火 自动开火默认开启 R 重开 ESC 退出", 颜色_白, 14) 如果 (游戏结束 == 真) { 画布_画文本(九S画布(), 270, 240, "游戏结束", 颜色_红, 40) 画布_画文本(九S画布(), 240, 300, "按 R 键重新开始", 颜色_白, 20) } } # 按键:方向键在 3D 空间移动、空格开火、R 重开、ESC 退出 函数 按键处理(键码) { 如果 (键码 == 键_左) { 玩家X = 玩家X - 玩家速度 播放音效文件("资源/move.wav") } 如果 (键码 == 键_右) { 玩家X = 玩家X + 玩家速度 播放音效文件("资源/move.wav") } 如果 (键码 == 键_上) { 玩家Y = 玩家Y + 玩家速度 播放音效文件("资源/move.wav") } 如果 (键码 == 键_下) { 玩家Y = 玩家Y - 玩家速度 播放音效文件("资源/move.wav") } 如果 (玩家X < -340) { 玩家X = -340 } 如果 (玩家X > 340) { 玩家X = 340 } 如果 (玩家Y < -40) { 玩家Y = -40 } 如果 (玩家Y > 170) { 玩家Y = 170 } 移动战机() 如果 (键码 == 键_空格) { 如果 (游戏结束 == 假 且 计时 - 上次开火 >= 开火间隔) { 发射() 上次开火 = 计时 } } 如果 (键码 == 键_R) { 初始化() } 如果 (键码 == 键_退出) { 关闭窗口() 返回 } 请求重绘() } # 每帧推进 函数 帧刷新() { 如果 (游戏结束 == 假) { 计时 = 计时 + 1 如果 (自动开火 == 真 且 计时 - 上次开火 >= 开火间隔) { 发射() 上次开火 = 计时 } 更新星空() 更新子弹() 生成敌机() 更新敌机() 检测碰撞() 更新粒子() 更新相机() } 请求重绘() } # 主程序 窗口 = 创建窗口("九艾斯3D打飞机 · 可改配置", 800, 600) 画布 = 九S画布(窗口) 初始化() 设置绘制函数(绘制界面) 设置按键函数(按键处理) 设置定时器(33, 帧刷新) 显示窗口() 请求重绘() 启动消息循环()