C#程序员整理的Unity 3D笔记(十):Unity3D的位移、旋转的3D数学模型

遇到一个想做的功能,但是实现不了,核心原因是因为对U3D的3D数学概念没有灵活吃透。故再次系统学习之—第三次学习3D数学.

 

本次,希望实现的功能很简单:

image

如在小地图中,希望可以动态画出Player当前的位置、z的朝向:用3条线、z轴正向、30°旋转、-30°旋转。

问题是:0点可以获得,P1点? P2点是未知的。

我尝试了2个小时,结果不竟如人意,少于沮丧。

image

 

不得不,再次花点时间系统的学习3D数学:

1 位移–向量和点:

  •    点: 点和向量在数学上是一致的,实际生活中点的概念比较好理解,坐标点来定位–南二环、北二环。   【点用(3,4,5)圆括号来标示,简称P】
  •    向量:“俗称增量”,有大小和方向,如”往前1步走。 左转90度”. 往前走,直到碰到墙,你才会停下来—在这之前,你无法准确获得碰到墙的点的坐标或者你和墙的距离                                              【向量用<3,4,5>尖括号来标示,简称V】
  • 在U3D中,统一用Vector3对象来表达向量和点,这个也是导致新手晕头转向的一个很重要的原因; 一个技巧,凡是在API中用position、Point的,V3肯定代表是点;凡是Vector、direction的是向量;多看官方的API手册,写得很明白。

做个游戏,列表出你用过的Vector3的API,分析分析用到的Vector3是向量(V)还是点(P)

需求

API

向量(V)

(P)

备注

平滑位移

Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta);

P

 

平滑位移

Vector3 Slerp(Vector3 from, Vector3 to, float t)

P

 

新的坐标点

this.m_transform.position = pos;

P

符合常规思维

变动一次坐标

m_transform.Translate(new Vector3(rx, 0, -m_Speed * Time.deltaTime));

V

可认为是增量

求方向

Vector3 relativePos = this.m_transform.position – m_Player.position;

V

向量减法,较难理解

设置新坐标点

lineRenderer.SetPosition(0,this.gameObject.transform.position);

m_NavAgent.SetDestination(this.m_Player.transform.position);

P

符合常规思维

新的位置

pointer.transform.position = hitInfo.point + (transform.position – hitInfo.point) * 0.01f;

P、V

力的叠加?

求距离

float dis = Vector3.Distance(v1, v2);

float f1 = (v1 – v2).sqrMagnitude;

float f3 = (v1 – v2).magnitude;

P、V

Distance用点即可

向量减法也可以

缩放正向

Vector3 v3 = this.m_transform.forward * 200f;

V

V的数乘

射线检测

Ray r = new Ray(source, dest);

Physics.Raycast(r, out hit, 1000, m_ShootMask)

V、P

Source:P

Dest:V

加一个力

rigidbody.AddForce (0, 10, 0);

V

 

 

依据这个游戏,可以整理出目前用到的有4个组件(含Vector 3)会发生位移:

位移常用的4个组件和Vector3

组件

函数

Unity 圣典的API说明

是否

用过

transform组件

translate函数

向某方向移动物体多少距离【默认local坐标系】

或者相对某物体移动

yes

 

Position属性

在世界空间坐标transform的位置

Yes

RigidBody组件

[FixedUpdate函数]

Velocity属性

刚体的速度向量

Unity 官方demo Done用的很酷

Yes

 

AddForce函数

添加一个力到刚体。作为结果刚体将开始移动。

yes

 

MovePosition函数

移动刚体到position

 

NavMeshAgent组件

SetDestination函数

设置自动Path目标点

yes

CharacterController

组件

Move函数

一个更加复杂的运动函数,每次都绝对运动

yes

 

SimpleMove函数

以一定的速度移动角色

 

Vector3向量

Lerp函数

两个向量之间的线性插值。

“像弹簧一个跟随目标物体”

 
 

Slerp函数

球形插值在两个向量之间

“在日出和日落之间动画弧线”

 
 

MoveToward函数

当前的地点移向目标

和Vector3.Lerp相同,maxDistanceDelta限速

 
 

SmoothDamp 函数

随着时间的推移,逐渐改变一个向量朝向预期的目标。

 

 

我的几个向量相关的问题:

  • 如何判断A向量和B向量是否同方向?
  • 如何判断A向量和B向量前、后、左、右?
  • 如何判断A向量和B向量的夹角?
  • 向量的减法貌似比加法更有用一些?
  • Vector.Forward和this.transform.Forward都表达local坐标系,那么数值为什么不一样呢?

 

2 旋转–Quaternion、eulerAngles、Quaternion.Euler

  

旋转在3D中是比较复杂的,在Unity 3D中一般用Quaternion来进行旋转, 旋转仅涉及向量的概念(向量的方向),请思考对于坐标点或者零向量旋转有无意义?

而按照Unity 3D API官方的说法,仅有约7个方法或者操作符比较常用,且占99%的概率,我截止目前还没有用到这么多,我用到的Quaterniong约有4个函数。

旋转常用7个API:

Quaternion API

Unity 圣典的API说明

是否

用过

Quaternion.LookRotation

创建一个旋转,沿着forward(z轴)并且头部沿着upwards(y轴)的约束注视。也就是建立一个旋转,使z轴朝向y轴朝向up。 常用的是transform.LookAt

yes

Quaternion.Angle

返回a和b两者之间的角度。

 

Quaternion.Euler

返回一个旋转角度,绕z轴旋转z度,绕x轴旋转x度,绕y轴旋转y度(像这样的顺序)。

yes

Quaternion.Slerp

球形插值,通过t值from向to之间插值。

 

Quaternion.FromToRotation

从fromDirection到toDirection创建一个旋转。

 

Quaternion.identity

返回恒等式旋转(只读)。这个四元数对于“无旋转”:这个物体完全对齐于世界或父轴。

yes

Quaternion.operator *

由另一个四元数来旋转一个旋转角度,或由一个旋转角度来旋转一个向量

yes

 

我的几个旋转相关的问题:

1 Quaternion.LookRotation和Vector3.RotateTowards的区别?

2 Quaternion.Angle和Vector3.Angle的区别?

3 Quaternion.LookRotation和transform.LookAt的区别?

[官方回答:   大多数时间你可以使用transform.LookAt代替

                  Quaternion.LookRotation]

4 如何实现2个GameObject face to face,即Z轴相对?

 

 

 

磨刀不误砍柴工: 经过一周的复习、反复验证,果然在系统学习3D 数学后,要实现的功能可以了,如下:

image

 

参考博客:

坐标系的简介:C#程序员整理的Unity 3D笔记(八):Unity 3D坐标系介绍

宣雨松的博客:http://www.xuanyusong.com/archives/1977

总结:

据说80%、90%Unity 3D程序员是自学的,大多数是看看书、实战视频、源码分析;而科班出生的同学会有3D数学这门核心课–估计占一个学期、会有作业等。为了赶上科班同学的水平,花点时间,补补数学知识,会使得自己少走一些弯路。一句话:”3D数学模型很关键。“

C#程序员整理的Unity 3D笔记(九):Unity 3D测试浅析

Unity中的组件上的类,是不能new的,必须通过组件去获取,new出来的东西是null。

if (amainfo.nameHash == Animator.StringToHash("Base Layer.idle"))

{

      m_animator.SetBool("Run", true);

}

代码无法执行时,在navigation面板中点击Bake,如下图

clip_image002

必须为碰撞体添加碰撞组件,否则Physics.Raycast函数无法检测到

删除destory时的时间,否则产生的多线程会导致计数不准确!

Unity3D自带测试工具,只需要在Asset Store中下载,然后导入,完成后菜单项会有Unity Test Tools项(如下图),完成了插件的导入,就可以进行单元测试了。

clip_image004

clip_image006

Unity3D的单元测试和VS2010的单元测试很几乎一样,如果接触过VS的单元测试,使用Unity3D的单元测试会简单很多。

1. 新建‘Editor‘文件夹,在文件夹中建一个单元测试的类,如下图

clip_image008

2. 在测试类文件中添加需要的测试方法,基本和VS中的单元测试一样,如下图所示:

clip_image010

3. 完成测试类的新建,点击Unity Test Tools 菜单中的Unit Test Runner 就可以进行单元测试,如下图:

clip_image012

4. 点击进行单元测试菜单后,进入测试界面,点击开始测试按钮,可以看到我们新建的测试类以及包含需要测试的方法,当时绿色的对号时,表示测试通过,否则表示测试失败,如下图:

clip_image014

以上就是Unity3D单元测试运行的基本步骤,如果熟悉VS开发平台的话,就会发现和VS的单元测试几乎是完全一样的。

集成测试Integration

由于单元测试只能测试单独函数的逻辑,无法模拟真正的游戏运行场景,所以一般游戏场景的模拟还是要用到集成测试。Unity3D的集成测试会调用Unity的运行函数,逐个测试用例。相当于实际游戏运行环境。

集成测试分为动态集成测试和普通集成测试

1. 普通集成测试,如果要测试某个GameObject,需要添加下图2的脚本组件,在clip_image016菜单中,添加完成后,可以对组件进行测试,运行clip_image018,即可以看到结果。如下图:

clip_image020

图1

clip_image022

图2

每一项集成测试都需要通过断言的组件的判断,这种普通测试只是用于一些简单的测试,如坐标移动范围,是否需要可见等。

动态集成测试

新建测试 New Test ,在它之下创建一个GameObject,创建TestScript脚本绑定在GameObject。 如果在该场景名下,动态测试脚本会自动挂载到测试面板中。如下图:

clip_image024

新建一个.cs文件里,创建若干个测试类。再针对不同模块,分成不同测试场景,进行测试,即可。也能支持NGUI等许多插件。按钮响应事件利用反射强制调用一下。

C#程序员整理的Unity 3D笔记(八):Unity 3D坐标系介绍

1World Space(世界坐标):
我们在场景中添加物体(如:Cube),他们都是以世界坐标显示在场景中的。
transform.position可以获得该位置坐标。
2Screen Space(屏幕坐标):
以像素来定义的,以屏幕的左下角为(0,0)点,右上角为(Screen.width,Screen.height),Z的位置是以相机的世界单位来衡量的。
Screen.width = Camera.pixelWidth
Screen.height = Camera.pixelHeigth
鼠标位置坐标属于屏幕坐标,Input.mousePosition可以获得该位置坐标,
手指触摸屏幕也为屏幕坐标,Input.GetTouch(0).position可以获得单个手指触摸屏幕坐标。
clip_image001

3ViewPort Space(视口坐标):
视口坐标是标准的和相对于相机的。相机的左下角为(0,0)点,右上角为(1,1)点,Z的位置是以相机的世界单位来衡量的。
clip_image002
4、绘制GUI界面的坐标系:
这个坐标系与屏幕坐标系相似,不同的是该坐标系以屏幕的左上角为(0,0)点,右下角为(Screen.width,Screen.height)。
clip_image003

【四种坐标系的转换】
1
、世界坐标屏幕坐标:
camera.WorldToScreenPoint(transform.position);
这样可以将世界坐标转换为屏幕坐标。其中camera为场景中的camera对象。
2、屏幕坐标视口坐标:
camera.ScreenToViewportPoint(Input.GetTouch(0).position);
这样可以将屏幕坐标转换为视口坐标。其中camera为场景中的camera对象。
3、视口坐标屏幕坐标:
camera.ViewportToScreenPoint();
4、视口坐标世界坐标:
camera.ViewportToWorldPoint();

C#程序员整理的Unity 3D笔记(七):接口、虚函数使用之个人见解

接口、虚函数使用之个人见解

最近在封装重构太空大战的代码,结合原来C#的知识点发现很多有意思的地方,此文主要介绍下接口和虚函数的使用,但对于接口和虚函数的区别这里不在描述(自行百度、谷歌)。

虚函数的使用:

定义一个Enemy的基类,它具有所有的敌人的共有属性及行为。而它的派生类会有较多的属性及特有/变种行。例如以下代码,Enemy具有生命、速度属性,有死亡、复活的行为,所有派生类也都具有这些属性及行为,把该类中的初始化方法,死亡、复活方法定义为虚方法,方便在派生类中有特殊逻辑可以在相应的重载函数中实现。

代码如下:

public class Enemy : MonoBehaviour {

public Transform m_transform;

///生命属性

public float m_life

{

get;

set;

}

///速度

public float m_speed

{

get;

set;

}

// Use this for initialization

void Start () {

Init();

m_transform = this.transform;

}

//初始化

protected virtual void Init()

{

m_speed = 1;

m_life = 5;

}

//移动轨迹

protected virtual void EnemyMove()

{

Move.Instance.MoveToDir(m_transform, CustomerMoveDirection.line,-m_speed);

}

//死亡

public virtual void Dead()

{

m_transform.position = Move.Instance.HidePosition;

m_transform.gameObject.SetActive(false);

}

//复活

public virtual void Revive(Vector3 position, Quaternion rotation)

{

m_transform.gameObject.SetActive(true);

m_transform.position = position;

m_transform.rotation = rotation;

}

}

派生类:

public class EnemyLevel1 : Enemy {

private float m_timer = 5f;

//生命及速度重写

protected override void Init()

{

m_life = 10;

m_speed = 0.5f;

}

//移动轨迹重写

protected override void EnemyMove()

{

#region 水平方向 z轴 1.5左右来回移动

if (m_timer > 0)

{

m_timer -= Time.deltaTime;

Move.Instance.MoveToDir(m_transform, CustomerMoveDirection.sign, m_speed);

}

else

{

Move.Instance.MoveToDir(m_transform, CustomerMoveDirection.sign,-m_speed);

if (m_transform.position.z > 4)

m_timer = 5f;

}

#endregion

}

public override void Dead()

{

//如果有特殊逻辑,可以在此处完成

base.Dead();

}

public override void Revive(Vector3 position, Quaternion rotation)

{

//如果 有特殊逻辑,可以再此处完成

base.Revive(position, rotation);

}

}

接口的使用:

定义IEnemy接口

public interface IEnemy

{

float m_life { get; set; }

float m_speed{get;set;}

void Dead();

void Revive(Vector3 position, Quaternion rotation);

}

类Enemy和EnemyLevel1 分别继承该接口(别忘了继承MonoBehaviour这个类),并在各自类中按各自的业务逻辑实现(当然公共部分可以抽出封装起来),此部分不贴代码(可以百度、谷歌 “C#接口实现”).

对比以上两种实现方式,个人感觉单就这个游戏的例子而言虚函数的使用要比接口方便,派生类中只需要在有特殊处理的方法中完善代码即可,但接口就需要在每个继承接口的类中去写实现。但如果要实现多继承的话,接口反而是最好的选择。

以上是个人在使用过程中的理解,有不对的地方欢迎大家指正、拍砖。 :)

C#程序员整理的Unity 3D笔记(六):Git源码管理

对程序员而言,写的代码用源码管理系统管理起来,非常方便:

  • 有历史可追溯
  • 方便对比代码变动。

非公司的项目,在没有了解git前,我采用是修改前Ctrl+C、Ctrl+V的方式进行备份、对比,这样导致很多奇奇怪怪的问题,例如手贱的改错了、巧合的是竟然没有备份,于是乎郁闷的查询……。浪费了不少时间。

随着Git的火热,我开始关注有无免费的git—-一个很重要的事实:

Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理,其背后的故事很有趣。

我了解的Git有3类git:

  • github.com: 公共Git项目免费,私有Git项目收费、在外国,网速上比较坑
  • git.oschina.net:公共、私有项目都免费,且在国内、网速占优势(广告:OSChina西安圈子管理员
  • Bitbucket :支持Git、Mercurial,免费版有5个用户限制。公有和私有仓库都可以无限制创建,有协作工具(Issue tracker,Wiki)。Unity 3D官方开源的地址

    我选择Git的关键:功能差不多情况下,尽量选择国内的,网速快、不被墙。

 

Git的客户端:

  • GitExtensions172SetupComplete.msi  27M:包含了Git的核心命令
  • TortoiseGit-1.8.11.0-64bit.msi 19M:基于上面Git命令的一个Shell,有GUI界面

Windows下配置GIT记住账号密码:

C:\Users\Administrator\.gitconfig中:

image

然后添加.git-credentials文件,配置账号密码即可:

image

 

完整的进行Unity 3D源码用Git管理的流程:

1 新建文件夹(路径要求非)

2 新建U3D项目

3 添加.gitignore文件

 

[获取已有的U3D项目]

4 添加https://git.oschina.net/xifarm/U3D_Git.git文件

5 修改Unity 3D编辑器:

      1. Enable External option in Unity → Preferences → Packages → Repository. (Skip this step in v4.5 and up)
      2. Switch to Visible Meta Files in Edit → Project Settings → Editor → Version Control Mode.
      3. Switch to Force Text in Edit → Project Settings → Editor → Asset Serialization Mode.
      4. Save the scene and project from File menu.

    Unity3D .gitignore(想忽略掉某个文件,不让这个文件提交到版本库中)

    # =============== #
    # Unity generated #
    # =============== #
    Temp/
    Library/
    
    # ===================================== #
    # Visual Studio / MonoDevelop generated #
    # ===================================== #
    ExportedObj/
    obj/
    *.svd
    *.userprefs
    /*.csproj
    *.pidb
    *.suo
    /*.sln
    *.user
    *.unityproj
    *.booproj
    
    # ============ #
    # OS generated #
    # ============ #
    .DS_Store
    .DS_Store?
    ._*
    .Spotlight-V100
    .Trashes
    ehthumbs.db
    Thumbs.db