My First RPG Game总结一
Posted zhangxiaofan666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了My First RPG Game总结一相关的知识,希望对你有一定的参考价值。
一进游戏把鼠标指针切换成想要的形状:
File-->Build settings,最下面有个player setting,在右侧属性中有个Default Cursor,拖动图片到那里即可
镜头很远观看物体时有雾的效果:
edit-->render setting,勾选fog,调整浓度
场景切换白色渐变效果做法:
在NGUI中创建一个simple texture,Texture选择纯白色,选中它,右键tween-->alpha,from为1,to为0由于进入游戏需要一段时间,Start Delay设为1秒
过一会要显示游戏名字,我们把标题的alpha先设为0,添加alpha的tween,让它一会显示出来,ping pong播放
给按任意键开始游戏这个按钮添加脚本
public class PressAnyKey : MonoBehaviour {
//按任意键开始是否按下
private bool start = false;
private GameObject button;
private void Start()
{
button = transform.parent.Find("ButtonContainer").gameObject;
}
void Update () {
if (start==false)
{
if (Input.anyKey)
{
showButton();
}
}
}
//显示新游戏和载入游戏
private void showButton()
{
button.SetActive(true);
this.gameObject.SetActive(false);
start = true;
}
}
这里注意的是
transform.parent.Find("ButtonContainer").gameObject;
拿到按钮的Gameobject要注意层级关系transform.parent.find
犯的错误; public GameObject[] characterPrefabs;将prefab中的Gameobject拖动到里面,那么请问还需要在代码中添加这段代码吗
characterPrefabs=new GameObject[characterPrefabs.length];
答:不能再添加这句话,否则报空指针
如果在最上面 private GameObject[] characterObjects;那么还需要new来初始化吗?
答,需要
按钮按下调用脚本注意要把调用的方法设成public才能调用
要在游戏屏幕的正中央创建人物角色,那么在中央点创建一个GameObject名为characterCreation,给它添加脚本。
在写脚本时候遇到的问题
public GameObject[] characterPrefabs;
private int nowIndex = 0;
private GameObject[] characterObjects;
private void Start()
{
int length = characterPrefabs.Length;
characterObjects = new GameObject[length];
for (int i= 0; i< characterPrefabs.Length; i++)
{
characterObjects[i]= Instantiate(characterPrefabs[i], characterPrefabs[i].transform.position, characterPrefabs[i].transform.rotation) ;
}
UpdateCharacterShow();
}
//显示当前的角色,隐藏其他职业的角色显示
void UpdateCharacterShow()
{
foreach (GameObject go in characterObjects)
{
go.SetActive(false);
}
characterObjects[nowIndex].SetActive(true);
}
这里characterPrefab是把人物模型从prefab中拖到数组中,但是如果我把updateCharacterShow方法中的characterObjects全部改成characterPrefabs程序不能运行,我觉得可能是characterObjects给new出来了,开辟了内存空间造成的,characterPrefabs只是声明了而已
接着在代码下面添加2个方法:
选择角色的左右键触发了两个方法来切换角色
在c#中能否在一个常量前面加static?
答,不能,
在c#中能否在一个变量前面加static?
答,可以,
我们一般这样声明一个常量:
如果这样声明一个常量的话,在另一个目录里面的脚本也可以调用该脚本的常量
但是java中好像就可以
public static final double PI=3.14159265358979323864;
鼠标点击地面产生的指针特效:
public class PlayerDir : MonoBehaviour {
//鼠标左键单击的特效拖过来
public GameObject effect_click_prefab;
void Update () {
if (Input.GetMouseButtonDown(0))
{
Ray ray= Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
//用一个bool来看有没有射线碰撞,如果有,而且碰撞到地面的话就显示鼠标左键的特效
bool isCollider = Physics.Raycast(ray,out hitInfo);
if(isCollider&&hitInfo.collider.tag==Tags.ground){
ShowClickEffect(hitInfo.point);
}
}
}
//如果鼠标点了一下,就实例化一个单击的特效
void ShowClickEffect(Vector3 hitPoint)
{
Instantiate(effect_click_prefab,hitPoint,Quaternion.identity);
}
}
如果鼠标左键的特效显示不完整就把hitPoint的y轴坐标加0.1f即可,
如果点击这个圆盘由于他的高度比较高,让鼠标特效显示不出来,给圆盘添加mesh collider,tags改成ground即可最后我们让鼠标左键即使按住也可以实时改变目标的朝向:
public class PlayerDir : MonoBehaviour {
//鼠标左键单击的特效
public GameObject effect_click_prefab;
//是否面向鼠标点击的位置
private bool isLookAt=false;
void Update () {
if (Input.GetMouseButtonDown(0))
{
Ray ray= Camera.main.ScreenPointToRay(Input.mousePosition);
//用一个bool来看有没有射线碰撞,如果有,而且碰撞到地面的话就显示鼠标左键的特效
RaycastHit hitInfo;
bool isCollider = Physics.Raycast(ray,out hitInfo);
if(isCollider&&hitInfo.collider.tag==Tags.ground){
ShowClickEffect(hitInfo.point);
isLookAt = true;
}
}
else if (Input.GetMouseButtonUp(0))
{
isLookAt = false;
}
if (isLookAt)
{
CharacterLookAtClick();
}
}
//猪脚面向鼠标
void CharacterLookAtClick()
{
//这里有个非常值得注意的地方,为什么不直接把上面的hitInfo传过来拿到目标点而是还要在函数里再做射线,
//因为GetMouseButtonDown只要按下只返回一次那里做的射线是按下那一刹那做出的目标点,
//如果按住鼠标需要通过Input.GetMouseButton来实时返回结果
//所以鼠标一直按住isLookAt是true,那么在这个函数再做一次射线检测才行
RaycastHit hitInfo;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
bool isCollider = Physics.Raycast(ray, out hitInfo);
if (isCollider && hitInfo.collider.tag == Tags.ground)
{
transform.LookAt(hitInfo.point);
}
}
//如果鼠标点了一下,就实例化一个单击的特效
void ShowClickEffect(Vector3 hitPoint)
{
Instantiate(effect_click_prefab,hitPoint,Quaternion.identity);
}
}
Update和LateUpdate和FixedUpdate的区别:
当MonoBehaviour启用时,其Update在每一帧被调用。
FixedUpdate 固定更新
当MonoBehaviour启用时,其 FixedUpdate在每一帧被调用。处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)
LateUpdate 晚于更新当Behaviour启用时,其LateUpdate在每一帧被调用。
LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。
Update和FixedUpdate的区别:
update跟当前平台的帧数有关,而FixedUpdate是真实时间,所以处理物理逻辑的时候要把代码放在FixedUpdate而不是Update.
Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关以及被渲染的物体(可以认为是三角形的数量)。在性能好的机器上可能fps 30,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢。因为Update的执行间隔不一样了。
而FixedUpdate,是在固定的时间间隔执行,不受游戏帧率的影响。有点想Tick。所以处理Rigidbody的时候最好用FixedUpdate。
PS:FixedUpdate的时间间隔可以在项目设置中更改,Edit->ProjectSetting->time 找到Fixedtimestep。就可以修改了。
Update和LateUpdate的区别
在圣典里LateUpdate被解释成一句话:LateUpdate是在所有Update函数调用后被调用。
LateUpdate是晚于所有Update执行的。例如:游戏中有2个脚步,脚步1含有Update和LateUpdate,脚步2含有Update,那么当游戏执行时,每一帧都是把2个脚步中的Update执行完后才执行LateUpdate 。虽然是在同一帧中执行的,但是Update会先执行,LateUpdate会晚执行。
现在假设有2个不同的脚本同时在Update中控制一个物体,那么当其中一个脚本改变物体方位、旋转或者其他参数时,另一个脚步也在改变这些东西,那么这个物体的方位、旋转就会出现一定的反复。如果还有个物体在Update中跟随这个物体移动、旋转的话,那跟随的物体就会出现抖动。 如果是在LateUpdate中跟随的话就会只跟随所有Update执行完后的最后位置、旋转,这样就防止了抖动。
做一个相机跟随主角的功能时,相机的位置调整写在LateUpdate()
对于人物的动画播放我们可以通过动画机,当然也可以直接用代码来实现动画播放,把动画拖动到player的animation中
在人物行走的代码如下:
public enum AnimState
{
run,
idle
}
public class PlayerDir : MonoBehaviour {
//鼠标左键单击的特效
public GameObject effect_click_prefab;
//是否面向鼠标点击的位置
private bool isLookAt=false;
//这个目标坐标是在另一个PlayerMove调用的坐标
public Vector3 TargetPos;
private CharacterController cc;
public float speed = 4;
//设置人物的动画状态
public AnimState playerAnimationState = AnimState.idle;
private void Start()
{
cc = GetComponent<CharacterController>();
//让角色一进游戏的目标位置为当前位置不发生移动
TargetPos = transform.position;
}
void Update () {
if (Input.GetMouseButtonDown(0))
{
Ray ray= Camera.main.ScreenPointToRay(Input.mousePosition);
//用一个bool来看有没有射线碰撞,如果有,而且碰撞到地面的话就显示鼠标左键的特效
RaycastHit hitInfo;
bool isCollider = Physics.Raycast(ray,out hitInfo);
if(isCollider&&hitInfo.collider.tag==Tags.ground){
ShowClickEffect(hitInfo.point);
isLookAt = true;
}
}
else if (Input.GetMouseButtonUp(0))
{
isLookAt = false;
}
if (isLookAt)
{
CharacterLookAtClick();
}
else
{
Move();
}
}
//猪脚面向鼠标
void CharacterLookAtClick()
{
//这里有个非常值得注意的地方,为什么不直接把上面的hitInfo传过来拿到目标点而是还要在函数里再做射线,
//因为GetMouseButtonDown只要按下只返回一次,如果按住鼠标需要通过Input.GetMouseButton来实时返回结果
//所以鼠标一直按住isLookAt还是true,那么在这个函数再做一次射线检测才行
RaycastHit hitInfo;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
bool isCollider = Physics.Raycast(ray, out hitInfo);
if (isCollider && hitInfo.collider.tag == Tags.ground)
{
transform.LookAt(hitInfo.point);
TargetPos = hitInfo.point;
}
}
//如果鼠标点了一下,就实例化一个单击的特效
void ShowClickEffect(Vector3 hitPoint)
{
Instantiate(effect_click_prefab,hitPoint,Quaternion.identity);
}
//角色移动
void Move()
{
//这里注意的是如果我直接 if (Vector3.Distance(TargetPos,transform.position)>0.1)那么鼠标点击的y轴坐标和
//人物的y轴坐标是有距离的即使走到了该点距离仍然>0.1所以要让TargetPos的y取人物的y坐标
Vector3 v = new Vector3(TargetPos.x,transform.position.y,TargetPos.z);
if (Vector3.Distance(v,transform.position)>0.1)
{
transform.LookAt(v);//这句话必须得加上,因为如果不加意思是鼠标抬起,朝着目标点行走,按理说没有错,
//但是如果遇到高地或者石头让人物行走的角度偏了一点那么会出现走到目标点仍然会行走的bug,所以要实时更新lookAt角度
cc.SimpleMove(speed*transform.forward);
playerAnimationState = AnimState.run;
}
else
{
playerAnimationState = AnimState.idle;
}
}
}
动画的代码如下:
public class PlayerAnim : MonoBehaviour {
private Animation anim;
private PlayerDir dir;
private void Start()
{
anim = GetComponent<Animation>();
//这里注意的是必须要拿到该脚本的组件,如果只是声明 private PlayerDir dir,直接调用dir.playerAnimationState
//那么会报空指针
dir = GetComponent<PlayerDir>();
}
private void LateUpdate()
{
//这里注意的是public enum AnimState要放到public class PlayerDir : MonoBehaviour的上面,
//这样在这个脚本中调用上个脚本就直接拿到AnimState,还有播放动画效果是在检测PlayerDir脚本的update函数的变量
//因此要使用lateUpdate函数。还要注意在public enum AnimState命名的时候会突然报错,可能是AnimState会与插件的类名重合了
if (dir.playerAnimationState== AnimState.idle)
{
anim.CrossFade("Idle");
}
else if(AnimState.run == dir.playerAnimationState)
{
anim.CrossFade("Run");
}
}
}
下面添加相机跟随人物行走脚本
private Transform player;
private Vector3 offsetPostion;
void Start () {
player = GameObject.FindGameObjectWithTag(Tags.player).transform;
offsetPostion = transform.position - player.position;
}
//这里注意要让相机的位置等于人物Position+offsetPosition,那么相机的更新位置要放到LateUpdate中防止抖动
//如果放到update中,那么会感觉一卡一卡的
private void LateUpdate()
{
transform.position = player.position + offsetPostion;
}
void Update () {
}
这个Name我们可以拿到它的名字
鼠标向前滑动input.GetAxis("Mouse ScrollWheel")为正值所以这样来在相机的脚本中这样做
//调整鼠标滚轮的视野拉近
void ScollView()
{
//视野的拉近改变的是相机镜头的距离角色的距离,而且这个距离是相机对角色的角度不变也即单位向量不变,改变长度
distance = offsetPostion.magnitude;
distance -= scrollSpeed * Input.GetAxis("Mouse ScrollWheel");
// offsetPostion.magnitude = distance;如果这样不行,向量的长度是只读的不能赋值,要用它的单位向量乘以距离
offsetPostion = offsetPostion.normalized * distance;
}
当然对于视距的拉近拉远也要有个限制添加一行代码来限制:
对于视野的左右旋转选用该函数,第一个参数是围绕哪个点旋转,第二个参数,围绕该点哪个轴旋转
if (isRotate)
{
//mouse x是左右滑动鼠标右键,围绕猪脚这个点,垂直于猪脚的轴旋转
transform.RotateAround(player.position,player.up,rotateSpeed*Input.GetAxis("Mouse X"));
//mouse x是上下滑动鼠标右键,围绕猪脚这个点,垂直于相机的左右轴旋转,这里非常容易出错,是相机的轴
transform.RotateAround(player.position, transform.right, -rotateSpeed * Input.GetAxis("Mouse Y"));
}
以上是关于My First RPG Game总结一的主要内容,如果未能解决你的问题,请参考以下文章