unity编程——小玩具

今天做了一个萤火虫飞舞效果,这个问题类似于一道算法题:一个点想移动到平面上任意一点,但是自身有一个旋转角度限制,每一次旋转时,最大旋转角度是 maxRotateAngle,思维延时(即两次连续执行自身指令的间隔)是 float gap。

大致思路是:

建立了四个指令,分别是:

Blink()—— 萤火虫屁股发光,是闪烁的......闪烁的...

Move()——(默认使用 Vector2 curSpeed 作为移动增量)

ComputeDir() —— 根据目标位置,计算当前运动方向,因为旋转角度限制无法一次直达。

Escape()—— 这个纯属自娱自乐了,哈哈,当鼠标接近萤火虫时,他就会“拼命地”逃离鼠标,我自己瞎搞的玩的。

这里只加了一直萤火虫,可以加更多。

(美术实在太差,,,凑合看看热闹吧....)

*******************************************************

技术细节:

1.转向问题有点棘手,因为要考虑很多方面。其中之一就是坐标系问题我在这个脚本里面用了Graphics.DrawTexture()函数,这个底层函数的坐标原点和其他的不太一样,是左上角为原点的。

如果不熟悉,可以用 print() 多测试几次,我就是这么搞的。(>_<)。

(改了好几次,写了好多个版本。最终敲定了现在手上的版本。)

2.其二考虑的是效率问题了,转向,三角函数是少不了用的,其中我想到了的一种方法以萤火虫自身为原点,前进方向为Y轴建立坐标系,然后进行计算。这个法子很笨的,因为后期还要把目标点切出来转换到屏幕空间,反而繁琐了;也许是我能力不够,没找到优化方法吧。

我所采用的方法是,直接在屏幕空间来进行计算。因为有了统一他标准,少了很多转换,优化手段也多了起来。具体来说也很繁琐。

优化无止境啊... 大概思路还是那句老话:用更少的空间代价,换取更快的速度。

3.说一说Graphics绘图:用过了几次后,(发现这个有点类似于Java的绘图,JAVA绘图很简单,应该说实际上不止JAVA各个绘图API底层都是这样的语法。)

这个底层绘图不能随便用,因为深度测试绕过去了,绘制顺序必须再自己统一规定,不利于模块化;

它的使用有一些点必须注意:

必须是这样的格式,在发生repaint 时才能绘制。联系JAVA、win32的知识,就知道了repaint,这绝对是在最后一个绘制的,前面的深度测试,顶点变换什么的他都不干,直接插队到最后,就是这样一个霸道的站在世界的顶点的男银。

————————————————————————————————————————————————

最后一环,贴代码吧:

自己写的也有很多不足,模块化不像模块化,函数之间的接替性不够,因为是我自己一个人玩玩,所以能用全局变量全部用的全局变量。还有很多,希望童鞋们不吝指教,多多与我交流,^_^

  1 using UnityEngine;
  2 using System.Collections;
  3
  4 public class Star : MonoBehaviour {
  5
  6     public Vector2 initPosPercent;
  7
  8     public float maxComputeDestGap = 1.0f;
  9     public float curComputeDestGap = 0.0f;
 10     public float computeDirGap = 0.5f;
 11     public float curComputeDirTime = 0.0f;
 12     // 纹理
 13     public Texture2D starBgd;
 14     private Rect bgdRT = new Rect(0,0,0,0);
 15     float maxWidth, maxHeight, width, height;
 16
 17     // 图片宽
 18     public float percentWidth = 0.1f;
 19
 20     // 速度最大值
 21     public float maxSpeedAmount = 0.2f;
 22
 23     // 当期速度值
 24     public float curSpeedAmount = 0.2f;
 25
 26     // 放大种子
 27     public float scaleSeed = 0.0f;    // 初始扩大系数
 28     public float maxScale = 0.8f, minScale = 0.5f;
 29
 30     // 最大旋转角度
 31     public float maxRotateAngle = 0.5f;    // 大约30度
 32     float curRotateAngle = 0.0f;
 33     // 当前速度
 34     private Vector2 curSpeed = new Vector2(0.0f,0.0f);
 35     // 当前缩放比
 36     private float curScale = 1.0f;
 37
 38     private float destX = 0.0f, destY = 0.0f;
 39
 40     // 常量
 41     private float halfPI;
 42     private float PI2;
 43
 44     // 逃跑警戒距离
 45     public float escapeDist;
 46     public float escapeSpeedAmount;
 47
 48     // Use this for initialization
 49     void Start () {
 50
 51         Vector3 pos = new Vector3 (Screen.width * initPosPercent.x, Screen.height * initPosPercent.y, 0.0f);
 52         transform.position = pos;
 53
 54         width = maxWidth = Screen.width * percentWidth;
 55         height = maxHeight = width;
 56
 57         bgdRT = new Rect ( transform.position.x, transform.position.y, width*curScale, height*curScale );
 58
 59
 60         halfPI = Mathf.PI * 0.5f;
 61         PI2 = Mathf.PI * 2.0f;
 62     }
 63
 64
 65     // Update is called once per frame
 66     void Update () {
 67
 68         if (curComputeDestGap < maxComputeDestGap )
 69             curComputeDestGap += Time.deltaTime;
 70         else
 71         {
 72             // 生成新目标
 73             destX = Random.Range (0, Screen.width);
 74             destY = Random.Range (0, Screen.height);
 75
 76             curComputeDestGap = 0.0f;
 77         }
 78
 79         if (curComputeDirTime < computeDirGap )
 80             curComputeDirTime += Time.deltaTime;
 81         else
 82         {
 83             // 生成新方向
 84             ComputeDir ( destX, destY);
 85
 86             curComputeDirTime = 0.0f;
 87         }
 88
 89
 90         // 逃离
 91         Escape ();
 92
 93         // 移动 star
 94         Move ();
 95
 96     }
 97
 98
 99     void OnGUI()
100     {
101         if( Event.current.type.Equals(EventType.Repaint) )
102         {
103             //    print ( x );
104             float x = transform.position.x - width * 0.5f;
105             float y = transform.position.y - height * 0.5f;
106             bgdRT = new Rect ( x, y, width, height );
107
108
109             GUI.DrawTexture ( bgdRT, starBgd );
110         }
111     }
112
113     void Blink()
114     {
115         curScale = (maxScale - minScale)* 0.5f * Mathf.Sin (Time.time + scaleSeed) + maxScale;
116
117         width = maxWidth * curScale;
118         height = width;
119     }
120
121     void Escape()
122     {
123         Vector2 pos = new Vector2 ( transform.position.x, transform.position.y );
124         Vector2 mousePos = new Vector2 ( 0, 0 );
125         mousePos.x = Input.mousePosition.x;
126         mousePos.y = Screen.height - Input.mousePosition.y;
127
128         float dist = Vector2.Distance ( mousePos, pos);
129
130         if( dist <= escapeDist )
131         {
132             Vector2 dir = new Vector2( 0.0f, 0.0f );
133
134             dir = pos - mousePos;
135
136             dir.Normalize();
137             curSpeed = dir * escapeSpeedAmount;
138         }
139     }
140
141
142     void Move ()
143     {
144         float t = transform.position.x + curSpeed.x;
145
146         // 防越界
147         if ( t < 0.0f || t > Screen.width )
148             curSpeed.x = -curSpeed.x;
149         t = transform.position.y + curSpeed.y;
150         if (t < 0.0f || t > Screen.height)
151             curSpeed.y = -curSpeed.y;
152
153
154         transform.Translate ( curSpeed.x * Time.deltaTime, curSpeed.y*Time.deltaTime, 0);
155     }
156
157     void ComputeDir( float srcX, float srcY )
158     {
159         // 根据源位置计算目标位置,旋转角度有限所致
160         float m = srcX - transform.position.x;
161         float n = srcY - transform.position.y;
162
163         // 在自身坐标系中的坐标
164         float angle = Mathf.Atan2 ( n, m );
165
166         float dAngle = angle - curRotateAngle;
167
168         // 转变成 pi < A - C < -PI
169         if (dAngle < -Mathf.PI) {
170             angle += PI2;
171         }
172         else if( dAngle > Mathf.PI )
173         {
174             angle -= PI2;
175         }
176
177         dAngle = angle - curRotateAngle;
178
179
180         // 计算最终角度
181         if (dAngle > -maxRotateAngle && dAngle < maxRotateAngle)
182         {
183             // 一步即达的目标方向
184             curRotateAngle = angle;
185         }
186         else
187         {
188             if( dAngle < 0.0f )
189             {
190                 curRotateAngle -= maxRotateAngle;
191             }
192             else
193             {
194                 curRotateAngle += maxRotateAngle;
195             }
196
197         }
198
199         // 转换到 -PI 到 PI 空间
200         if (curRotateAngle < -Mathf.PI) {
201             curRotateAngle += PI2;
202         }
203         else if( curRotateAngle > Mathf.PI )
204         {
205             curRotateAngle -= PI2;
206         }
207
208         // 计算最终速度
209         curSpeed.x = curSpeedAmount * Mathf.Cos ( curRotateAngle );
210         curSpeed.y = curSpeedAmount * Mathf.Sin ( curRotateAngle );
211
212         return;
213     }
214
215 }

***************

吐硼:

C#真是够了,虐的我压灭压灭的,一说全是泪啊....

时间: 2024-10-12 13:31:38

unity编程——小玩具的相关文章

【Unity编程】四元数(Quaternion)与欧拉角

欧拉旋转.四元数.矩阵旋转之间的差异 除了欧拉旋转以外,还有两种表示旋转的方式:矩阵旋转和四元数旋转.接下来我们比较它们的优缺点. 欧拉角 优点:三个角度组成,直观,容易理解. 优点:可以进行从一个方向到另一个方向旋转大于180度的角度. 弱点:死锁问题. 前面<[Unity编程]欧拉角与万向节死锁(图文版)>已经介绍过万向节死锁问题. 四元数 内部由四个数字(在Unity中称为x,y,z和w)组成,然而这些数字不表示角度或轴,并且通常不需要直接访问它们.除非你特别有兴趣深入了解四元数学,你只

【Unity编程】Unity中关于四元数的API详解

Unity中关于四元数的API详解 Quaternion类 Quaternion(四元数)用于计算Unity旋转.它们计算紧凑高效,不受万向节锁的困扰,并且可以很方便快速地进行球面插值. Unity内部使用四元数来表示所有的旋转. Quaternion是基于复数,并不容易直观地理解. 不过你几乎不需要访问或修改单个四元数参数(x,y,z,w); 大多数情况下,你只需要获取和使用现有的旋转(例如来自"Transform"),或者用四元数来构造新的旋转(例如,在两次旋转之间平滑插入). 大

hadoop编程小技巧(4)---全局key排序类TotalOrderPartitioner

Hadoop代码测试版本:Hadoop2.4 原理:在进行MR程序之前对输入数据进行随机提取样本,把样本排序,然后在MR的中间过程Partition的时候使用这个样本排序的值进行分组数据,这样就可以达到全局排序的目的了. 难点:如果使用Hadoop提供的方法来实现全局排序,那么要求Mapper的输入.输出的key不变才可以,因为在源码InputSampler中提供的随机抽取的数据是输入数据最原始的key,如下代码(line:225): for (int i = 0; i < splitsToSa

积累的VC编程小技巧之对话框

1.用鼠标移动基于对话框的无标题栏程序的简单方法 void CVCTestDlg::OnLButtonDown(UINT nFlags, CPoint point) {    //一句话解决问题     SendMessage(WM_SYSCOMMAND,0xF012,0);    CDialog::OnLButtonDown(nFlags, point);} 2.对话框消息映射 有对话框A,B从A中发消息给B然后B处理.准备工作,先定义消息,如下#define WM_B_NOTIFY WM_U

积累的VC编程小技巧之工具提示

1.用鼠标移动基于对话框的无标题栏程序的简单方法 void CVCTestDlg::OnLButtonDown(UINT nFlags, CPoint point) {    //一句话解决问题     SendMessage(WM_SYSCOMMAND,0xF012,0);    CDialog::OnLButtonDown(nFlags, point);} 2.对话框消息映射 有对话框A,B从A中发消息给B然后B处理.准备工作,先定义消息,如下#define WM_B_NOTIFY WM_U

积累的VC编程小技巧之树操作

1.如何在TreeList中加图标? [问题提出]  请问treeview控件和treectrl控件的用法有何不同呢?向如何imagelist控件中加图象呀?  [解决方法]  1)    HICON hicon[8];    m_imageList.Create(16,16,0,8,8);    hicon[0]=AfxGetApp()->LoadIcon(IDI_ICON0);    hicon[1]=AfxGetApp()->LoadIcon(IDI_ICON1);    hicon[2

积累的VC编程小技巧之图标、光标及位图

1.图标透明 (1).Windows中的图标其实是有两个图像组成的,其中一个用于与它要显示的位置的图像做"AND"操作,另一个作"XOR"操作. 透明:用"白色"AND,用"黑色"XOR反色:用"白色"AND,用"白色"XOR正常色:用"黑色"AND,用正常颜色XOR.(2). WIN9X中好像是对像素的操作实现透明的WIN2K中就有API直接实现透明了!WIN2K中

积累的VC编程小技巧之框架窗口及其他

1.修改主窗口风格 AppWizard生成的应用程序框架的主窗口具有缺省的窗口风格,比如在窗口标题条中自动添加文档名.窗口是叠加型的.可改变窗口大小等.要修改窗口的缺省风格,需要重载CWnd::PreCreateWindow(CREATESTRUCT& cs)函数,并在其中修改CREATESTRUCT型参数cs.CWnd::PreCreateWindow 函数先于窗口创建函数执行.如果该函数被重载,则窗口创建函数将使用CWnd::PreCreateWindow 函数返回的CREATESTRUCT

积累的VC编程小技巧之文件操作

1.删除文件夹 // 删除文件夹及其所有内容void CBaseDoc::RemoveFolder(const CString &strPathName){    CString path = strPathName;    if (path.Right(1) != _T("\\"))        path += _T("\\");    path += _T("*.*");    CFileFind ff;    BOOL res =