微信小游戏 彩色试管 倒水游戏 逻辑 (二)

 最近开始研究微信小游戏,有兴趣的 可以关注一下 公众号, 记录一些心路历程和源代码。

定义一个 Water class

1. **定义接口和枚举**:
   - `WaterInfo` 接口定义了水的颜色、高度等信息。
   - `PourAction` 枚举定义了水的倒动状态,包括无动作、加水、倒水。

2. **类 `Water`**:
   - `Water` 类继承自 `Component`,用于控制水杯中的水。
   - 包含了私有变量 `_action` 用于记录当前倒动状态,`infos` 数组用于存储每一层水的信息,`stopIdx` 和 `curIdx` 分别记录停止倒水和当前水层的索引。
   -初始化方法 `initInfos` 和 `addInfo`,用于设置和添加水层信息。
   - `setPourOutCallback` 和 `setPourInCallback` 方法,用于设置倒水和加水的回调函数。
   - `getPourStartAngle` 和 `getPourEndAngle` 方法,用于计算倒水的起始和结束角度。
   - `onStartPour` 方法,用于开始倒水。
   - `update` 方法,用于每帧更新水的状态。
   - `addStep` 和 `pourStep` 方法,用于每帧增加或减少水的高度。
   - `initSizeColor` 和 `updateAngleHeight` 方法,用于初始化和更新材质属性。
   - showDebugCenter` 方法,用于调试显示水面中心点。

3. **辅助函数**:
   - `angle2radian` 和 `radian2angle` 函数用于角度和弧度的转换。

### 实现原理

- **材质和着色器**:通过 `Material` 和 `EffectAsset` 来应用自定义的着色器效果,模拟水的倒动效果。
- **物理模拟**:通过每帧更新水的高度来模拟水的流动,使用三角函数计算倾斜角度和水面中心点。
- **回调函数**:通过设置回调函数,可以在倒水和加水的特定时刻执行特定的操作。

### 用途

这段代码可以用于游戏或应用中模拟水杯中水的倒动效果,例如在游戏中模拟饮料的倒动,或者在应用中模拟水杯的倒水效果。

import { Color, Component, EffectAsset, Label, Material, Sprite, UITransform, v2,Node, v3, _decorator, Vec4, color, v4, log } from "cc";
import { DEV, EDITOR } from "cc/env";const { ccclass, property, requireComponent, executeInEditMode, disallowMultiple, executionOrder } = _decorator;export interface WaterInfo{colorId:number,color:Color,//颜色height:number,//默认情况下,占杯子的高度
}const MAX_ARR_LEN = 6;enum PourAction{none,/**往里加水 */in,/**向外倒水 */out,
} @ccclass
@requireComponent(Sprite)
@executeInEditMode
@disallowMultiple
@executionOrder(-100)
export default class Water extends Component {private _action:PourAction = PourAction.none;private infos:WaterInfo[] = [];/**到这里停止倒水 */private stopIdx = -1;/**当前是有几层水 */private curIdx = 0;/**节点高宽比 */private _ratio:number = 1;@property(EffectAsset)private effect:EffectAsset = null;@property private _skewAngle: number = 0;@property({ tooltip: DEV && '旋转角度' })public get skewAngle() { return this._skewAngle; }public set skewAngle(value: number) { value = Math.round(value*100)/100;// log("angle",value)this._skewAngle = value; this.updateAngleHeight();}private _material: Material = null;private get material(){if(this._material==null){let sp = this.node.getComponent(Sprite);if(sp){if (sp.spriteFrame) sp.spriteFrame.packable = false;// 生成并应用材质if(this.effect){this._material = new Material();this._material.initialize({effectAsset:this.effect})sp.setMaterial( this._material,0);}this._material = sp.getSharedMaterial(0)this._material.setProperty("mainTexture",sp.spriteFrame.texture)}}return this._material}protected onLoad() {this._ratio = this.node.getComponent(UITransform).height/this.node.getComponent(UITransform).width;}public initInfos(infos:Array<WaterInfo>){this.infos = infos;this.curIdx = this.infos.length-1;this.initSizeColor();this.updateAngleHeight();}private addHeight = 0;public addInfo(info:WaterInfo){this.addHeight = info.height;info.height = 0;this.infos.push(info);this._action = PourAction.in;this.curIdx = this.infos.length-1;this.initSizeColor();}private onOutStart:Function = null;private onOutFinish:Function = null;public setPourOutCallback(onOutStart:Function,onOutFinish:Function){this.onOutStart = onOutStart;this.onOutFinish = onOutFinish;}private onInFInish:Function = null;public setPourInCallback(onInFInish:Function){this.onInFInish = onInFInish;}/*** 倾斜到哪个角度开始往外边倒水*/public getPourStartAngle(){let _height = 0;for(let i=0;i<=this.curIdx;i++){_height+=this.infos[i].height;}return this.getCriticalAngleWithHeight(_height);}/*** 倾斜到哪个角度开始停止倒水(当前颜色的水倒完了)*/public getPourEndAngle(){this.stopIdx = this.curIdx-this.getTopSameColorNum();let _height = 0;for(let i=0;i<=this.stopIdx;i++){_height+=this.infos[i].height;}return this.getCriticalAngleWithHeight(_height);}/**获取某一高度的水刚好碰到瓶口的临界倾斜角度 */private getCriticalAngleWithHeight(_height){let ret = 0;if(_height==0){ret = 90;return ret;}if(_height<0.5){//水的体积小于杯子的一半,先碰到下瓶底let tanVal = this._ratio/(_height*2.0);ret = Math.atan(tanVal);}else{let tanVal = 2.0*this._ratio*(1.0-_height); ret = Math.atan(tanVal);}ret = radian2angle(ret);return ret;}private getTopSameColorNum(){let sameColorNum = 0;let colorId=null;for(let i=this.curIdx;i>=0;i--){if(colorId==null){sameColorNum++;colorId = this.infos[i].colorId;}else if(this.infos[i].colorId==colorId){sameColorNum++;}else{break;}}return sameColorNum}/*** 开始倒水* 一直倒水直到不同颜色的水到达瓶口,为当前最大能倾斜的角度* @returns 返回值为倾斜角度的绝对值*/public onStartPour(){this._action = PourAction.out;this.stopIdx = this.curIdx-this.getTopSameColorNum();}update(){if(this._action==PourAction.out){this.pourStep();}else if(this._action==PourAction.in){this.addStep()}}/*** 每帧调用,升高水面高度*/addStep(){if(this.curIdx<0){return;}let info = this.infos[this.curIdx];info.height = Math.round((info.height + 0.005)*1000)/1000;// log("--------info.height",info.height)if(info.height>=this.addHeight){info.height = this.addHeight;this._action = PourAction.none;if(this.onInFInish){this.onInFInish();this.onInFInish = null;}}this.updateAngleHeight();}/*** * 每帧调用* * 降低水面高度 */pourStep(){if(this.curIdx<0){this._action = PourAction.none;return;}let _height = 0;for(let i=0;i<=this.curIdx;i++){_height+=this.infos[i].height;}let is_top = false;let angle = (this.skewAngle%360) * Math.PI / 180.0let _t = Math.abs(Math.tan(angle));if(_height<0.5){//水的体积小于杯子的一半,先碰到下瓶底is_top = _t>(this._ratio)/(_height*2.0);}else{is_top = _t>2.0*this._ratio*(1.0-_height);}let info = this.infos[this.curIdx];if(!is_top){//没到瓶口,不往下倒if(info.height<0.05){//可能还留了一点点水,要继续倒出去}else{return;}}if(this.onOutStart){this.onOutStart();this.onOutStart = null;}info.height = Math.round((info.height - 0.005)*1000)/1000;if(info.height<0.01){info.height = 0;this.infos.pop();this.curIdx--;// log("------this.curIdx",this.curIdx,this.stopIdx)if(this.curIdx==this.stopIdx){if(this.onOutFinish){this.onOutFinish();this.onOutFinish = null;}this._action = PourAction.none;}}// log("this.curIdx",this.curIdx,"info.height",info.height.toFixed(2),"angle",this.skewAngle.toFixed(2))this.updateAngleHeight();}private initSizeColor(){for(let i=0;i<this.infos.length;i++){const c = this.infos[i].color; this.material.setProperty('color'+i, c);}let size = v2(this.node.getComponent(UITransform).width,this.node.getComponent(UITransform).height)this.material.setProperty('sizeRatio', size.y/size.x);this.material.setProperty('waveType', 0);this.material.setProperty("layerNum",this.infos.length)}private updateAngleHeight() { for(let i=0;i<6;i++){if(i<this.infos.length){let h = this.infos[i].height;this.material.setProperty('height'+i, h);}else{this.material.setProperty('height'+i, 0);}}let radian = angle2radian(this._skewAngle)this.material.setProperty('skewAngle', radian*1.0);let waveType = 0.0;if(this._action==PourAction.in){waveType = 1.0;}else if(this._action==PourAction.out){waveType = 2.0;}this.material.setProperty('waveType', waveType);this.material.setProperty("layerNum",this.infos.length)this.showDebugCenter();}private dot:Node = null;/**显示水面的中心点,调试shader脚本用 */private showDebugCenter(){if(EDITOR){return;}if(this.dot==null){this.dot = new Node();this.dot.parent = this.node;this.dot.addComponent(UITransform)let label = this.dot.addComponent(Label);label.string = "·";label.fontSize = 60;label.color = Color.RED;}let ratio = this.node.getComponent(UITransform).height/this.node.getComponent(UITransform).width;let angle = angle2radian(this.skewAngle);let _height = 0;if(this.curIdx>=this.infos.length){return}for(let i=0;i<=this.curIdx;i++){_height+=this.infos[i].height;}let toLeft = Math.sin(angle)>=0.0;let center = v2(0.5,1.0-_height);//水面倾斜时,以哪个店为中心店let _t = Math.abs(Math.tan(angle));if(_height<=0.5){//水的体积小于杯子的一半,先碰到下瓶底let is_bottom = _t>ratio*2.0*_height;//倾斜角度达到瓶底if(is_bottom){center.x = Math.sqrt((2.0*_height/_t)*ratio)/2.0;center.y = 1.0 - Math.sqrt((2.0*_height*_t)/ratio)/2.0;let is_top = _t>(ratio)/(_height*2.0);//倾斜角度达到瓶口if(is_top){center.y = 0.5;center.x = _height;}}if(!toLeft){center.x = 1.0-center.x;}if(Math.abs(center.x-0.25)<0.01){let i = 0;}// log("aa-------center",center.x.toFixed(2),center.y.toFixed(2));}else{//水比较多,先碰到上瓶底let is_top = _t>2.0*ratio*(1.0-_height);if(is_top){center.x = Math.sqrt(2.0*ratio*(1.0-_height)/_t)/2.0;center.y = Math.sqrt(2.0*ratio*(1.0-_height)*_t)/2.0/ratio;let is_bottom = _t>ratio/(2.0*(1.0-_height));if(is_bottom){center.y = 0.5;center.x = 1.0-_height;}}else{}if(toLeft){center.x = 1.0-center.x;}// log("bb-------center",center.x.toFixed(2),center.y.toFixed(2));}center.x = center.x - 0.5;center.y = -center.y + 0.5;let pt = v3(center.x*this.node.getComponent(UITransform).width,center.y*this.node.getComponent(UITransform).height);this.dot.position = pt;}
}function angle2radian(angle:number){while(angle>360){angle-=360;}while(angle<-360){angle+=360;}return (angle%360) * Math.PI / 180.0;
}function radian2angle(radian:number) {return radian/Math.PI*180;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/46542.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C双指针元素去重

需求 在尾部插⼊、删除元素是⽐较⾼效的&#xff0c;时间复杂度 是 O(1)&#xff0c;但是如果在中间或者开头插⼊、删除元素&#xff0c;就会涉及数据的搬移&#xff0c;时间复杂度为 O(N)&#xff0c;效率较低。 代码 #include <stdio.h>// 相邻元素去重 int remove…

01 电场强度通量 高斯定理

电场强度通量 高斯定理 5-4 电场强度通量 高斯定理一.电场线二.电场强度通量三.高斯定理四高斯定理应用举例典型电场的电场线分布图形正点电荷与负点电荷的电场线一对等量正点电荷的电场线一对等量异号点电荷的电场线一对不等量异号点电荷的电场线带电平行板电容器的电场线 5-4…

CompletableFuture介绍与实战

CompletableFuture 介绍与实战 一、前言 ​ 日常工作中&#xff0c;大多数情况下我们的接口的执行逻辑都是串行化的&#xff0c;串行化的逻辑也基本能满足我们绝大部分的场景。但是&#xff0c;在一些情况下我们的代码可能会存在一些比较耗时的操作&#xff0c;串行的逻辑就有…

金蝶云星空与金蝶云星空对接集成付款单查询打通[标准][付款单新增]-v1

金蝶云星空与金蝶云星空对接集成付款单查询打通[标准][付款单新增]-v1 对接源平台:金蝶云星空 金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上&#xff0c;提供了标准的管理模式&#xff1b;通过标准的业务架构&#xff1a;多会计准则、多币别、多地点、多组织、多税制应用…

Gstreamer学习3.1------使用appsrc灌颜色信号数据

这个视频内容讲解的离散余弦变换&#xff0c;讲的很好&#xff0c; 离散余弦变换可视化讲解_哔哩哔哩_bilibili 其中讲到&#xff0c;把颜色变化转换为曲线的处理&#xff0c; 在前面的学习中&#xff0c;我们知道了可以向appsrc来灌数据来进行显示 Gstreamer学习3----灌数据…

yolo格式数据集之野生动物类4种数据集已划分好|可以直接使用|yolov5|v6|v7|v8|v9|v10通用

本数据为野生动物类检测数据集&#xff0c;数据集数量如下&#xff1a; 总共有:1504张 训练集&#xff1a;1203张 验证集&#xff1a;150张 类别数量&#xff1a;4 测试集&#xff1a;151 类别名&#xff1a; [‘buffalo’, ‘elephant’, ‘rhino’, ‘zebra’] 占用空间&…

自动驾驶-端到端分割任务

上采样 bed of nails interpolation transposed convolutions 1. 上采样 (Upsampling) 上采样是一种技术&#xff0c;用于增加数据集中的样本数量或是提高信号的分辨率。在图像处理中&#xff0c;上采样通常指的是增加图像的像素数量&#xff0c;从而使图像变得更大。这可…

如何用STM32实现modbus-RTU?

Modbus RTU是一种广泛应用于工业自动化领域的通信协议,基于主从架构,通过串行通信进行数据传输。本文将详细介绍Modbus RTU协议的基本原理,并提供在STM32微控制器上实现Modbus RTU通信的完整代码示例。 1. Modbus RTU协议概述 Modbus RTU的定义和特点 Modbus RTU(Remote Te…

哥德巴赫猜想c++

方法一 #include<bits/stdc.h> using namespace std; //定义函数&#xff0c;判断素数 bool sushu(int n){bool rtrue;//先假设是素数&#xff0c;即真//循环因子范围&#xff0c;找到一个因子就不是素数for(int i2;i<sqrt(n);i){//判断2~n的根号是否素数if(n%i0){//…

【Linux】从零开始认识多线程 --- 线程控制

在这个浮躁的时代 只有自律的人才能脱颖而出 -- 《觉醒年代》 从零开始认识多线程 --- 线程控制 1 知识回顾2 线程控制2.1 线程创建2.2 线程等待2.3 线程终止 3 测试运行3.1 小试牛刀 --- 创建线程3.2 探幽析微 --- 理解线程参数3.3 小有心得 --- 探索线程返回3.4 求索无厌 …

数据结构_顺序表专题

何为数据结构&#xff1f; 咱今天也来说道说道...... 数据结构介绍 准确概念 数据结构就是计算机存储、组织数据的方式 概念分析 从上句分析&#xff0c;数据结构是一种方式。一种管理数据的方式。为了做什么&#xff1f;为的就是计算机存储数据&#xff0c;组织数据。 …

元器件基础学习笔记——磁珠

一、磁珠的作用及构造 1.1 磁珠的作用 磁珠是一种用于抑制高频噪声的被动电子组件&#xff0c;通常由铁氧体材料制成&#xff0c;这种材料具有高电阻率和高磁导率&#xff0c;使其能够在高频下有效地将干扰信号以热能的形式消耗掉。在电路设计中&#xff0c;磁珠被广泛用于信号…

LeetCode 441, 57, 79

目录 441. 排列硬币题目链接标签思路代码 57. 插入区间题目链接标签思路两个区间的情况对每个区间的处理最终的处理 代码 79. 单词搜索题目链接标签原理思路代码 优化思路代码 441. 排列硬币 题目链接 441. 排列硬币 标签 数学 二分查找 思路 由于本题所返回的 答案在区间…

3d复制的模型怎么渲染不出来?----模大狮模型网

在展览3D模型设计领域&#xff0c;技术的进步和创新使得模型的复杂性和精细度有了显著提升。然而&#xff0c;有时设计师们在尝试渲染复杂的3D复制模型时&#xff0c;却面临着无法正确呈现的问题。模大狮将探讨这一现象的可能原因&#xff0c;并提供相应的解决方案和建议&#…

知识图谱和 LLM:利用Neo4j驾驭大型语言模型(探索真实用例)

这是关于 Neo4j 的 NaLLM 项目的一篇博客文章。这个项目是为了探索、开发和展示这些 LLM 与 Neo4j 结合的实际用途。 2023 年,ChatGPT 等大型语言模型 (LLM) 因其理解和生成类似人类的文本的能力而风靡全球。它们能够适应不同的对话环境、回答各种主题的问题,甚至模拟创意写…

3d导入模型后墙体变成黑色?---模大狮模型网

在展览3D模型设计领域&#xff0c;技术和设计的融合通常是创意和实现之间的桥梁。然而&#xff0c;有时设计师们会遇到一些技术上的挑战&#xff0c;如导入3D模型后&#xff0c;墙体却突然变成了黑色。这种问题不仅影响了设计的视觉效果&#xff0c;也反映了技术应用中的一些复…

【UE5.3】笔记10-时间轴的使用

时间轴 右键--Add Timeline(在最下面) --> 双击进入时间轴的编辑界面&#xff1a; 左上角可以添加不同类型的轨道&#xff0c;可以自定义轨道的长度&#xff0c;单位秒&#xff0c;一次可以添加多个 可以通过右键添加关键帧&#xff0c;快捷键&#xff1a;shift鼠标左键按…

C++基础语法:链表和数据结构

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 链表是最基础的数据集合,对标数组.数组是固定长度,随机访问,链表是非固定长度,不能随机访问.数组查找快,插入慢;链表是插入快,查找慢. 前面推导过"数据结构算法数据集合".想建立一个数据集合,就要设计数…

鸿蒙 画布来了 我不允许你不会

前言: 作者:徐庆 团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合作…

WPF+MvvmLight 项目入门完整教程(一)

WPF+MvvmLight入门完整教程一 创建项目MvvmLight框架安装完善整个项目的目录结构创建自定义的字体资源下载更新和使用字体资源创建项目 打开VS2022,点击创建新项目,选择**WPF应用(.NET Framework)** 创建一个名称为 CommonProject_DeskTop 的项目,如下图所示:MvvmLight框架…