找回密码
 立即注册
楼主: zxhhst

[脚本] Unity3d 自带的游戏主角控制分析。

  [复制链接]
发表于 2013-7-5 14:08 | 显示全部楼层 |阅读模式
本帖最后由 zxhhst 于 2013-7-8 15:43 编辑

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image002.gif
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image002.gif

1.上面是正弦函数。Sin(Q)  
Q<180度Y值是正数,
Q=90度为1,
Q=180度为0,
Q>180度为负数。
Q=270为-1,
百度知识
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image003.jpg

2.上面是余弦函数.Cos(Q)
Q=0度为1,
Q<90度Y值为正数,
Q=90度为0,
90<Q<270度为负数,
180度为-1.
270<Q<360度为正数
百度知识




3.unity3d用的是左手坐标系。百度知识。
左手坐标系

存在两种完全不同的3D坐标系。
伸出左手,让拇指和食指成“L”形,大拇指向右,食指向上。其余的手指指向前方。这样就建立了一个左手坐标系。拇指、食指和其余手指分别代表x,y,z轴的正方向。
判断方法:在空间直角坐标系中,让左手拇指指向x轴的正方向,食指指向y轴的正方向,如果中指能指向z轴的正方向,则称这个坐标系为左手直角坐标系.反之则是右手直角坐标系。
大拇指向右,x轴都是正数,x>0。反方向为负数,x<0;
食指向上,y轴都是正数,y>0。反方向为负数,y<0;
其余手指前方,z轴都是正数,z>0。反方向为负数,z<0;
4.先确定好,现在讲2d和3d向量: {x,y,z},

    在数学课本里向量是数字列表。2D向量:(3,4)3D向量:(3,4,5).
    在程序猿中是另一个相似的概念-数组,a[2]={3,4};b[3] = {3,4,5};
Unity3d:
Vector2 point=newVector2(3,4),  Vector3 v3 = new Vector3(3,4,5).

    向量和标量:
    比如:“速度”和“位移”是向量。“速率”和“长度”是标量。
   
5.向量的几何定义:
代数还真的复杂和麻烦。我真的佩服搞代数的人。
几何是非常伟大的东西,它能让你一目了然,看了就能理解的。
2d向量可以这样表达。在平面上起点为圆心的射线。
3d 向量可以这样表达,在球面上起点为圆心的射线。
向量的大小是向量的长度(模) 如:2d ( x^2+y^2 根号2 ),非负长度。
向量的方向描述空间中向量的指向。不完全等同于方位。但是乎有联系。
位移: “向前走3步”,更位置没有任何关系,天知道你在那里,那个位置,
但是你前面是空旷就可以”向前走3步”。它没有绝对的位置,
只有位移的大小(三步)和方向(向前)。
速度:“我开着拖拉机向南以每小时20里的速度奔驰”,在你老家也
可以开拖拉机向南以每小时20里的速度奔驰,在北京的长安街可以开
拖拉机向南以每小时20里的速度奔驰。它没有绝对的位置,
只有速度的大小(每小时20里)和方向(向南)。
向量和点:
点有位置,但是没有实际的大小和厚度。向量有大小和方向没有位置。
相对位置。在个位置必须有参照物。她在我的南边。但是也没有绝对的位置,
对她来讲,站在比她更南的人,她就在北边。
“如果从东面看她,她则处在西面,从南面看她,她则处在北面”佛的楞严经。
奇怪的是茫茫宇宙没有绝对的坐标。好像对我们来说没有知道的必要。
比如有人找我的时候,我在东街口向南100米的肯德鸡店等你。你只要先
东街口,就很快在到肯德鸡店。东街口在那里你心里知道就可以了。
绝对的位置管他呢。


标量和向量相乘:
  n {1,1,1} = {n,n,n};
几何解释向量可以放大缩小长度。



向量的标准化:
对于许多向量我们只有知道方向而不关心大小的时候。单位向量就很方便。
单位向量就是大小为1的向量。单位向量经常也叫向量的标准化。
Unity3d 的函数是:Vector3.Normalize();这么简单,擦!
学几何代数的人会昏死。


向量的加减法。
x,y,z.
A=[1,0,0];
B=[0,1,0];
C =[0,0,1];
D = A+B+C= [1,1,1];
几何解释就是x方向平移1y方向平移1z方向平移1.
向量的加减法就是三维空间中,上下左右前后方向平移。
Unity3d例子1:
  Vector3 moveY = new Vector3(0,5,0);
transform.position = moveY;
对象y方向平移5。就是向上移5
Unity3d例子2:
  transform.Translate(Vector3.up*1*Time.deltaTime);
对象按时y方向平移1。就是向上移1
Vector3.up是世界坐标Y


向量点乘:
两个向量可以相乘。 {x,y,z}* {i,j,k} =  x*i+y*j+z*k;
几何解释:
向量a·向量b=|a||b|cos<a,b> ( <a,b> 指向量a与向量b之间的夹角)
点乘结果描述了两个向量的“相似”程度,
注意向量点乘的结果越大,两个向量越相近。
特别注意:这个夹角q(<a,b>)有妙用。
积        夹角           向量a和向量b的关系。
     >0   0<q<90     方向基本相同
     =0   q=90       正交
     <0   90<q<180方向基本相反。
这个可以用来作主角自身旋转正反方向的。
    Unity3d的函数表达:
    var v3dot = Vector3.Dot (dirA, dirB);



向量投影:
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image006.jpg
如图, dira向量投影y向量,分解出平行于y,和垂直y的两个分量:我们先叫它
v1(平行于y) v2 垂直y.   一般称v1dirAy的投影。



向量差乘:
两个向量的叉乘(也被称作外积和向量积)将产生另一个垂直于这两个向量的向量,如图4.13所示。以下仅给出三维的叉乘运算:
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image008.jpgfile:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image010.jpg
叉乘的大小
叉乘结果向量的大小是每个被乘向量大小的积再乘以两向量夹角的正弦值。(这与点乘大小的定义十分相似,不过要把余弦换成正弦)。

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image012.jpg
四元数:
[w,{x,y,z}].   W是标量,一个3d向量分量x,y,z.
3d中,四元数作为旋转对象角度的工具。
下面是用插值,对对象用四元数绕x轴旋转30度,绕y轴旋转90度。
transform.rotation =Quaternion.Slerp(transform.rotation,Quaternion.Euler(30.0f,90.0f,0.0f), Time.time * 0.1f);


欧拉角:
角位于分解绕三个互相垂直轴的三个旋转组成的序列。听起来复杂,其实非常直观。章动角 θ、旋进角(即进动角)ψ和自转角j组成。
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image013.jpg
给定方位的表达方式不唯一。
两个角度间求插值非常困难。
360度会很麻烦。
Unity3d运用:
得到主摄像机绕Y轴旋转的欧拉角。Transform.eulerAngles.y相对世界坐标系的旋转角。注意:如果是控制第一人物主角,一定是摄像头z轴方向是运动前后方向,否则人就会走的歪的,那就是很怪了。
mainCameraTransform.eulerAngles.y


下面我们对unity3d在带的官方游戏Angrybots对主角的控制脚本进行分析:
主角身上有三个脚本:MovementMotor.js PlayerMoveController.js FreeMovementMotor.js
做了的三件事:
1. 主角的前后左右移动
2. 人物自身旋转。鼠标围着主角转,主角也会自身旋转
3. 摄像头也随着鼠标前后左右移动,感觉场景在圆滑的拖动。


FreeMovementMotor.js 继承于MovementMotor.js。
MovementMotor.js脚本里有三个共用向量。
public var movementDirection : Vector3;//人物移动向量
public var movementTarget :Vector3;  //运动目标
public var facingDirection : Vector3;    //脸的转向。


下面我们来分析主角的前后左右移动。
//人物控制左右前后,
/*
为什么要摄像头的旋转来当作主角前后左右行动的基本向量呢?
在游戏里摄像头就是眼睛,如果主角不随我们的左右上下方便,我们就不好控制。这是正确的。如果走斜了,那还是游戏吗?
private var screenMovementSpace : Quaternion;//四元数
private var screenMovementForward : Vector3; //前后移动单位向量
private var screenMovementRight : Vector3;   //左右移动单位向量


//四元数,将摄像机的世界Y轴方向的旋转角转换为四元数
screenMovementSpace = Quaternion.Euler (0,mainCameraTransform.eulerAngles.y, 0);


将此四元数与世界坐标系中的Z轴方向的单位向量相乘,就是摄像头Z轴方向在世界坐标系中
单位向量。
screenMovementForward = screenMovementSpace * Vector3.forward; //前后移动单位向量


四元数与世界坐标系中的X轴方向的单位向量相乘,就是摄像头X轴方向在世界坐标系中
单位向量。就是主角坐标的移动向量。
screenMovementRight = screenMovementSpace * Vector3.right;     //左右移动单位向量


知道了前后左右单位向量,按键值乘左右移动向量和前后移动向量再相加。
就是主角每次移动向量。
motor.movementDirection = Input.GetAxis("Horizontal") * screenMovementRight + Input.GetAxis("Vertical") * screenMovementForward;


// walkingSpeed是每次移动的速度,是标量,上面有人。得到移动目标向量。
var targetVelocity : Vector3 = movementDirection * walkingSpeed;  //这个是步距
      
//变量增量速率  =  移动目标向量 - 刚体的速度向量。
var deltaVelocity : Vector3 = targetVelocity - rigidbody.velocity;
      
//控制重力是否影响整个刚体
if (rigidbody.useGravity)
       deltaVelocity.y= 0;  //重力不起作用时  y轴的向上下力为0
              
//添加一个力到刚体。作为结果刚体将开始移动。 刚体是主角。walkingSnappyness是50,也是标量。.
//人物添加了刚体。
rigidbody.AddForce (deltaVelocity *walkingSnappyness, ForceMode.Acceleration);




//人物自身旋转。鼠标围着主角转,主角也会自身旋转。        
public var cursorPlaneHeight :float = 0;


// Set the initial cursor position to the center of the screen
//在屏幕中间,设置初试化屏幕光标位置
//开始2d屏幕中心点 z;
cursorScreenPosition= Vector3 (0.5 * Screen.width, 0.5 * Screen.height, 0);
      
// cachingmovement plane 建立垂直于Y轴一个主角本身坐标平面
//人的位置平面(法向量)y高度*当前地板的高度(是否上下坡也算在内)。
playerMovementPlane = new Plane (character.up,character.position + character.up * cursorPlaneHeight);  


// On PC, the cursor point is the mouse position
//得到鼠标的2d坐标,z = 0; 就是屏幕坐标。
varcursorScreenPosition : Vector3 = Input.mousePosition;


// Find out where the mouse ray intersectswith the movement plane of the player
//建立鼠标到射像头坐标的一射线。射线
//射线穿过垂直于Y轴一个主角本身坐标平面的那个点,就是旋转目标世界坐标。
varcursorWorldPosition : Vector3 = ScreenPointToWorldPointOnPlane(cursorScreenPosition, playerMovementPlane, mainCamera);


// The facing direction is the direction fromthe character to the cursor world position
//选择脸的目标向量 = 旋转目标世界坐标 - 主角向量.
motor.facingDirection = (cursorWorldPosition -character.position);


//y方向设0,不受上下影响   
motor.facingDirection.y = 0;                  




// Setup player to facefacingDirection, or if that is zero, then the movementDirection
varfaceDir : Vector3 = facingDirection;  //脸的方向
if(faceDir == Vector3.zero)
        faceDir= movementDirection;  //运动方向
              
//Make the character rotate towards the target rotation
if(faceDir == Vector3.zero) {
                  rigidbody.angularVelocity= Vector3.zero; //刚体的角速度向量
}
else{
/*
  * transform.forward 主角的当前的z轴的向量   
  * faceDir选择脸的目标向量
  *           
  */                                          
var rotationAngle :float = AngleAroundAxis (transform.forward, faceDir, Vector3.up); //旋转角
rigidbody.angularVelocity= (Vector3.up * rotationAngle * turningSmoothing); //刚体的角速度向量= Vector3.up*   旋转角 * 流畅的转动
}
}
      
//The angle between dirA and dirB around axis
//这个角围绕轴心axis在两个角之间
//dirA transform.forward 主角的当前的z轴的向量   
//dirB faceDir已选择脸的目标向量
  staticfunction AngleAroundAxis (dirA : Vector3, dirB : Vector3, axis : Vector3) {


// Project A and B onto the planeorthogonal target axis
//分力三角形法则. 投影一个向量到另一个向量,
  dirA = dirA - Vector3.Project (dirA, axis);


// 分力三角形法则. 投影一个向量到另一个向量
   dirB = dirB - Vector3.Project (dirB, axis);


// Find (positive) angle between A and B
//找到a和b向量的夹角
  var angle : float = Vector3.Angle (dirA,dirB);   


// Return angle multiplied with1 or -1
//Cross  两个向量的交叉乘积
file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image014.jpg
//夹角<180, 叉乘后得到向量为正数。
//夹角>180. 叉乘后得到向量为负数。
//点乘。Vector3.Dot。b=|a||b|cos<a,b>,实际上axis和叉乘积无非是正反两方向。
//正方向和反方向 angle或 – angle.
return angle * (Vector3.Dot (axis,Vector3.Cross (dirA, dirB)) < 0 ? -1 : 1); //Vector3.Cross 叉乘


}
public static function ScreenPointToWorldPointOnPlane (screenPoint :Vector3, plane : Plane, camera : Camera) : Vector3 {
//Set up a ray corresponding to the screen position
//屏幕位置转射线,是2d的
varray : Ray = camera.ScreenPointToRay (screenPoint);

//Find out where the ray intersects with the plane
returnPlaneRayIntersection (plane, ray);
}  


3. 摄像头也随着鼠标前后左右移动,感觉场景在圆滑的拖动。      
    最关键的是,通通的回到2d平面上来。首先得到鼠标的2d坐标,
    然后,得屏幕中心点坐标,就是2d坐标轴上原点坐标. 鼠标位置和平面中心点坐标一减就知道要向那个方向移动多少(矢量就得到了)。
    移动不能一下子移过去,那就会闪动,所以要慢慢的过去(圆滑的移过去),类似单位向量就很重要,
    移动因子(X方向的比例,y方向的比例下面程序里有讲)。
// On PC, the cursor point is the mouse position
//得到鼠标的2d坐标,z = 0; 就是屏幕坐标。
varcursorScreenPosition : Vector3 = Input.mousePosition;

//屏幕的一半宽。
var halfWidth : float = Screen.width / 2.0f;


//屏幕的一半高。
var halfHeight : float = Screen.height / 2.0f;


//得到(半宽和半高)那个比较长的值。
varmaxHalf : float = Mathf.Max (halfWidth, halfHeight);


                     
// Acquire the relative screen position         
//获得相关的屏幕坐标      
//Vector3 (halfWidth, halfHeight,cursorScreenPosition.z) 中心点


//得到光标位置2d的.z=0; cursorScreenPosition
//屏幕中心点2d的,z=0; Vector3 (halfWidth, halfHeight,cursorScreenPosition.z);   
//posRel就是 光标位置和屏幕中心点的差值。看是3d,实际是2d的
varposRel : Vector3 = cursorScreenPosition - Vector3 (halfWidth, halfHeight,cursorScreenPosition.z);         


//X方向的比例。 = 差值/ maxHalf。有点标准化向量的意思。微积分知道嘛.
posRel.x /= maxHalf;


//Y方向的比例 =差值/ maxHalf,注意等一下当z方向。前后方向。
posRel.y /= maxHalf;


// screenMovementRight 摄像头左右移动单位向量
// screenMovementRight 摄像头左右移动单位向量
//这一下看明白了吧。摄像头在X方向和z方向的微调。
//那个比例为什么这么重要,就是圆滑的移动。
//摄像头调整向量cameraAdjustmentVector
cameraAdjustmentVector= posRel.x * screenMovementRight + posRel.y * screenMovementForward;


//y方向设0  ,不受上下影响   
cameraAdjustmentVector.y = 0.0;   

// Draw the cursor nicely
//画一个老鼠在屏幕
HandleCursorAlignment (cursorWorldPosition);  


//public var cameraPreview : float = 2.0f;  //摄相机移动参数。
// HANDLE CAMERA POSITION
// Set the target position of the camera to point at the focus point
//initOffsetToPlayer = mainCameraTransform.position -character.position;  //摄相头初始化到主角的偏移
//这个是摄相头的目标位置 = 主角位置+摄相头初始化到主角的偏移+摄像头调整向量
var cameraTargetPosition : Vector3 = character.position +initOffsetToPlayer + cameraAdjustmentVector * cameraPreview;


//Apply some smoothing to the camera movement
//设置摄像头的位置。
//SmoothDamp平滑尼阻,随着时间的推移,逐渐改变一个向量朝向预期的目标
//当前的位置mainCameraTransform.position
//我们试图接近的位置cameraTargetPosition
//当前速度,这个值由你每次调用这个函数时被修改cameraVelocity
//到达目标的大约时间,较小的值将快速到达目标 cameraSmoothing = 0.01
mainCameraTransform.position= Vector3.SmoothDamp (mainCameraTransform.position, cameraTargetPosition,cameraVelocity, cameraSmoothing);


//Save camera offset so we can use it in the next frame
//保存摄像头偏移 = 当前摄像头位置 – 主角位置。;
cameraOffset= mainCameraTransform.position - character.position;



评分

参与人数 2 +2 收起 理由
yugo215 + 1 很给力!
graywolfx21 + 1

查看全部评分

 楼主| 发表于 2013-7-5 14:12 | 显示全部楼层
本帖最后由 zxhhst 于 2013-7-8 14:13 编辑

排版太差,图没有装进来。好好看,认真看,对比上下看,愚人的一点浅知,大家见笑了。
发表于 2017-3-16 09:28 | 显示全部楼层
楼主是超人
发表于 2017-3-16 09:43 | 显示全部楼层
真心顶
发表于 2017-3-16 09:37 | 显示全部楼层
难得一见的好帖
发表于 2017-3-16 10:14 | 显示全部楼层
很好哦
发表于 2017-3-16 09:56 | 显示全部楼层
不错不错
发表于 2017-5-6 09:56 | 显示全部楼层
很不错
发表于 2017-5-6 09:33 | 显示全部楼层
顶顶多好
发表于 2017-5-6 09:58 | 显示全部楼层
真心顶
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-30 21:21 , Processed in 0.342359 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表