2016-02-23 14:56:48 wsc122181582 阅读数 1735

开发项目:RPG黑暗之光单机


开发软件:Unity5.3+VS2015


开发时间:30天


开发过程:

1.场景搭建

2.开始界面,角色选择界面

3.角色控制(移动,视角,视野,状态机)

4.功能模块

(1)任务

(2)背包,物品

(3)状态

(4)装备

(5)技能

(6)商店(武器,药品)

5.游戏界面(头像,血量,经验,地图,功能栏,快捷栏)

6.敌人控制(状态机)

7.战斗系统、技能系统


开发技术:UGUI、DOTween、读取txt文档、

2013-04-19 17:10:45 iteye_20251 阅读数 211

一、创建Unity项目

       打开Unity游戏编辑器界面,在导航菜单栏中选择File --->  New Project菜单项,在弹出界面中选择Create new Project页面,将项目名命名为MobilityModel,然后再点击Create Project,完成项目的创建。

       然后点击保存(Ctrl+S)保存场景。

 

二、构建模型

       在Hierarchy视图中分别创建游戏对象Plane(面板)、Cube(立方体)、Sphere(球体)、Cylinder(圆柱体)、Capsule(胶囊体),然后鼠标拖动创建的游戏对象将他们摆放在合适的位置。

       接下来在游戏中添加一个光源,在Hierarchy视图中选择Create--->Directional light菜单项,光源是游戏中非常重要的一个属性,一定要在游戏场景中设置它,如果不设置光源对象,Game视图会非常暗,严重影响游戏的效果。

 

三、添加脚本

       在Project视图中点击Create--->JavaScript菜单项创建一个游戏脚本,将其命名为MobilityModelScript.js,在脚本上编写如下代码:

//模型移动速度
var TranslateSpeed = 10;
//模型旋转速度
var RotateSpeed = 1000;
//绘制UI界面
function OnGUI(){
	//设置GUI背景色
    GUI.backgroundColor = Color.red;
	if(GUI.Button(Rect(10,10,70,30),"向左旋转")){
		//向左旋转
		transform.Rotate(Vector3.up * Time.deltaTime * (-RotateSpeed));
	}
	if(GUI.Button(Rect(90,10,70,30),"向前移动")){
		//向前移动
		transform.Translate(Vector3.forward * Time.deltaTime * TranslateSpeed);
	}
	if(GUI.Button(Rect(170,10,70,30),"向右旋转")){
		//向右旋转
		transform.Rotate(Vector3.up * Time.deltaTime * RotateSpeed);
	}
	if(GUI.Button(Rect(90,50,70,30),"向后移动")){
		//向后移动
		transform.Translate(Vector3.forward * Time.deltaTime * (-TranslateSpeed));
	}
	if(GUI.Button(Rect(10,50,70,30),"向左移动")){
		//向左移动
		transform.Translate(Vector3.right * Time.deltaTime * (-TranslateSpeed));
	}
	if(GUI.Button(Rect(10,50,70,30),"向右移动")){
		//向右移动
		transform.Translate(Vector3.right * Time.deltaTime * TranslateSpeed);
	}
	
	//显示模型位置信息
	GUI.Label(Rect(250,10,200,30),"模型位置"+transform.position);
	//显示模型旋转信息
	GUI.Label(Rect(250,50,200,30),"模型旋转"+transform.rotation);
}

      上述代码中重要方法和属性如下:

      OnGUI()方法用来绘制GUI界面组件。

      GUI.Button():设置一个按钮,返回true时表示该按钮被按下。

      GUI.Label():设置一个文本框。

      transform:为当前绑定模型的变换对象。

      transform.Rotate():设置模型旋转。

      transform.Translate():设置模型平移。

      Time.deltaTime:该数值为一个只读属性,不可修改,表示完成最后一帧的事件,单位为秒。

      Vector3:标志一个模型移动或者旋转的方法。

      Rect:规定一个矩形区域,用于显示控件。

      将编写好的脚本对象,将其从Project视图拖拽到Hierarchy视图中的立方体(Cube)对象上,如果没有提示错误,表示脚本绑定成功,运行游戏后该游戏对象将执行该脚本中的内容。

 

       目前立方体对象与其他模型对象之间是不存在碰撞的,但是运行游戏后,可以控制立方体(Cube)直接穿越另一个模型对象。为了让模型对象之间具有物理的碰撞,需要给模型对象添加一个刚体(Rigidbody)属性,添加方式:首先在Hierarchy视图中选中立方体对象,在Uinty导航菜单栏中选择Component --> Physics 

-->Rigidbody菜单项即可。

 

四、测试运行

       点击运行按钮就能看到结果,可以点击显示的六个按钮对象,可以移动Cube(立方体)的位置和旋转。


 

2016-06-13 20:02:35 woaini454186694 阅读数 4741

【Unity3D实战】RPG黑暗之光:游戏分解及各系统的实现


一、任务系统:
1、添加接任务的老爷爷:
Model中选择并添加老爷爷,调整方向,并添加一个Box collider组件。

2、设计任务UI界面:
UI Root添加一个Sprite(命名为Quest),选择Altas图集里的Quest对话框,添加Tween Position动画效果为从右进入。

3、设计任务内容:
选择Quest,添加一个Lable-->Child,设置字体、字号,字的内容。
添加Accept和Cancel按钮:选择Quest,添加Sprit-->Child,Attach-->Box Collider,Attach-->Button Script,同上面添加Button操作一样。
在点击UI界面时,主角不能移动的处理:在人物移动脚本里面加上一个条件,UICamera.hoveredObject == null,鼠标在UI界面上,这个条件不成立,就不能移动。
添加一个Close按钮:同上。

4、处理任务系统对话框的显示和隐藏:
给老爷爷添加一个脚本,NPCBar.cs
public TweenPosition questTween;//把UI Quest拽给此变量
void OnMouseOver() {//当鼠标位于这个collider之上的时候,会在每一帧调用这个方法
    if (Input.GetMouseButtonDown(0)) {//当点击了老爷爷
        ShowQuest();
    }
}
void ShowQuest() {
    questTween.gameObject.SetActive(true);
    questTween.PlayForward();
}
void HideQuest() {
    questTween.PlayReverse();
}
//任务系统:任务对话框上的按钮点击事件的处理,把这个事件注册到Close按钮上。
public void OnCloseButtonClick() {
    HideQuest();
}
注册OnCloseButtonClick事件到Close按钮上:把Bar_NPC拖到Close按钮的On Click-->Notify上,并选择对应Method为OnCloseButtonClick

二、功能面板(右下角):
Bag
Status
Equip
Skill
Setting

三、信息管理系统:
物品信息:即创建一个Text文本文件存放物品即信息。
角色信息:等级、物品、经验值、装备、技能等要存储读取。

四、背包系统:

五、状态系统(主角状态信息):

六、装备系统:

七、技能系统:

八、主角头像和HP/MP显示(左上角):

九、小地图(右上角);

十、药店商人及药品系统:

十一、武器商人及武器系统:

十二、敌人系统:
小狼、中狼、大狼;
敌人自动孵化;
敌人AI以及自动寻路;
战斗时的特效;
敌人动画系统;
杀敌之后物品拾取;

十三、等级系统:
杀敌之后增加经验;
升级之后状态更新;


2018-11-13 11:42:32 zhangxiao13627093203 阅读数 960

一、前言

     我们最近要做一个线路的规划编辑,并且是在三维场景中,编辑完就立马能用。立马能用还好说,有特别多的轮子可以用,在三维场景中实时编辑就有点意思了。其实功能就是类似于在Unity的编辑界面操作一个Cube的位置,当然旋转什么的我这个任务里暂时还不需要,就先简单的做了一个位置的三维拖拽。如图所示:在Unity的编辑界面对一个Cube进行拖拽,选中中心就可以进行任意拖

拽,选中单个的坐标轴可以进行指定方向的拖拽。

下面是我实现的效果图,如同所示:

二、实现

1、任意拖拽的比较简单,主要思路是控制好鼠标按下和正在拖拽的坐标差,以及鼠标的坐标到世界坐标的转换,代码如下

 mr.material.color = IsBeSelected ? beSelectedColor : normalColor;

        if(Input.GetKeyDown(KeyCode.Mouse0))
        {
            screenPosition = Camera.main.WorldToScreenPoint(gameObject.transform.position);
            offset = gameObject.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z));
            isMouseDrag = true;
        }
        if(Input.GetKey(KeyCode.Mouse0))
        {
            if (isMouseDrag&&IsBeSelected)
            {
                //Debug.Log("开始拖拽了");
                Vector3 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z);
                Vector3 currentPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace) + offset;
                gameObject.transform.position = currentPosition;
            }
        }
        if(Input.GetKeyUp(KeyCode.Mouse0))
        {
            isMouseDrag = false;
        }

2、三个坐标轴方向的拖拽,比较复杂,但是鼠标的移动计算是一样的,只是利用这个的最终位置量需要考虑一下,不能直接使用,需要考虑在每个轴方向的投影。比如X轴,那么就需要考虑这个X轴模型所代表的方向是它的Transform的前、上还是右方向,我这里的X模型的前方向是代表它在世界坐标所指向的方向,然后就将得到的鼠标拖着后的位置变化坐标投影到这个方向,但是其他方向的投影还是原来的位置坐标。代码如下:

 if (Input.GetKeyDown(KeyCode.Mouse0))
        {
            dragBeforeGameObjPos = parentTransform.transform.position;
            screenPosition = Camera.main.WorldToScreenPoint(dragBeforeGameObjPos);
            offset = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z));
            isMouseDrag = true;
        }
        if (Input.GetKey(KeyCode.Mouse0))
        {
            if (isMouseDrag && IsBeSelected)
            {
                //Debug.Log("开始拖拽了");
                Vector3 currentScreenSpace = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPosition.z);
                Vector3 currentPosition = Camera.main.ScreenToWorldPoint(currentScreenSpace) + offset;
                float tempLength = Vector3.Distance(currentPosition, transform.position);
                Vector3 tempPos = Vector3.zero;
                switch (curPAType)
                {
                    case PointAxis_Type.Axis_X:
                        tempPos = Vector3.Project(currentPosition, transform.forward) + Vector3.Project(dragBeforeGameObjPos, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.right);
                        break;
                    case PointAxis_Type.Axis_Y:
                        tempPos = Vector3.Project(currentPosition, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.right) + Vector3.Project(dragBeforeGameObjPos, transform.forward);
                        break;
                    case PointAxis_Type.Axis_Z:
                        tempPos = Vector3.Project(currentPosition, transform.right) + Vector3.Project(dragBeforeGameObjPos, transform.up) + Vector3.Project(dragBeforeGameObjPos, transform.forward);
                        break;
                }
                parentTransform.transform.position = tempPos;
            }
        }
        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            isMouseDrag = false;
        }

3、最后在来一段选中控制操作

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlCam : MonoBehaviour
{
    public static ControlCam M_Instance
    {
        get
        {
            if(null==_instance)
            {
                _instance = FindObjectOfType<ControlCam>();
            }
            return _instance;
        }
    }

    public bool M_IsCanControl
    {
        get
        {
            return isCanControl;
        }
    }

    public static ControlCam _instance;
    private bool isMouseDrag = false;
    private Vector3 lastMousePos;
    [SerializeField]
    private Vector3 offset;

    private bool isCanControl = false;
    private PointAxis PA;
    private PointEntity PE;
    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.LeftControl))
        {
            isCanControl = true;
        }
        if (Input.GetKeyUp(KeyCode.LeftControl))
        {
            isCanControl = false;
        }
        if (Input.GetKeyDown(KeyCode.Mouse1))
        {
            lastMousePos = Input.mousePosition;
            isMouseDrag = true;
        }
        if (Input.GetKey(KeyCode.Mouse1))
        {
            if (isMouseDrag)
            {
                offset = Input.mousePosition - lastMousePos;
                transform.RotateAround(Vector3.zero, Vector3.up, offset.x);
                transform.RotateAround(Vector3.zero, Vector3.right, offset.y);
                lastMousePos = Input.mousePosition;
            }
        }
        if (Input.GetKeyUp(KeyCode.Mouse1))
        {
            isMouseDrag = false;
        }
        if (Input.GetKeyUp(KeyCode.Mouse0))
        {
            isCanControl = false;
        }
        if (!isCanControl)
        {
            Reset_SelectedState();
            return;
        }
        Ray tempRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(tempRay, out hit, 2000))
        {
            PointEntity tempHitPE = hit.collider.GetComponent<PointEntity>();
            PointAxis tempHitPA = hit.collider.GetComponent<PointAxis>();
            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                if (null != tempHitPE)
                {
                    Reset_SelectedState();
                    PE = tempHitPE;
                    PE.IsBeSelected = true;
                }
                else if (null != tempHitPA)
                {
                    Reset_SelectedState();
                    PA = tempHitPA;
                    PA.IsBeSelected = true;
                }
            }
        }
    }

    private void Reset_SelectedState()
    {
        if (null != PE)
        {
            PE.IsBeSelected = false;
            PE = null;
        }
        if (null != PA)
        {
            PA.IsBeSelected = false;
            PA = null;
        }
    }
}

选中单个坐标轴或中心就让其高亮,每个坐标轴和中心都只能选中一个,为了避免在拖拽滑动鼠标的时候会选中其他的坐标轴或者中心,而造成混乱,这里我让鼠标按下的时候才触发选中操作。

三、总结

1、轮子有就好,没有也得学会自力更生

2、基本实现了类似Unity的编辑界面操作拖拽物体的功能,但是操作的设计上还需要有待加强

3、摄像机的旋转是为了观察在不同姿态下,坐标轴的拖拽始终是朝着它所指向的方向

4、工程下载地址CSDN下载地址

2018-07-02 18:15:19 weixin_42590843 阅读数 2929

# 前言

D最近正在参考官方的英文文档进行BehaviorDesigner插件的研究学习.这篇文章既是一篇教学,也是一篇学习笔记.我会努力做到条理清晰,图文并茂. 希望可以帮助大家更便捷快速的掌握这款如此优秀的插件. 


# Behavior Designer 是什么?

Behavior Designer 是一款极其优秀的行为树插件. 它提供了非常强大的界面交互系统,可以帮助你轻而易举的创建各种任务行为.它拥有一套极其直观的交互界面,并可以与多种第三方插件配合,帮助你创造无比强大的智能AI系统.甚至完全不必书写代码.

你使用这款插件的时候,虽然不必了解整个行为树系统是如何实现的,但是了解其中的一些关键概念还是非常重要的.比如说:行为树的任务节点类型.

为树结构由4种节点类型构成.

    Action 行为节点
    Composite 复合节点
    Conditional 条件节点
    Decorator 装饰节点

你安装好Behavior Designer插件并在Unity中打开它时,会看到下面这个界面. (见参考图: 01)         


# Behavior Designer 界面构成是怎样的?

Behavior Designer 的界面窗口总共分为4大区域 (见参考图:02)


1图表区域这就是你用来创建行为树的地方. 也是你在Behavior Designer工作时的主要操作区域. 在这个区域你可以创建新的任务节点,并手动的将这些任务节点部署到行为树当中去. 
2属性区域这里的功能比较多样,可以编辑行为树属性.建立变量.修改参数.快捷创建任务节点等等...
3工具栏区域你可以在这里通过下拉菜单快速选择现有的行为树文件. 或者快捷的添加/删除/保存/导出行为树文件.
4调试信息区域用于显示Debug信息.帮助用户进行问题调试.并且你可以在这里通过 开始/停止/步进按钮控制Unity的播放进程.


# 如何创建<Behavior Tree组件>和<任务节点>?

一开始,我们首先要添加一个Behavior Tree组件,该组件将自动成为你接下来创建的行为树文件的管理器. Behavior Tree组件创建完成后,它会自动绑定到你当前选择的Game Object上.

创建Behavior Tree组件有两种方法.

方法A在图表区域 - 按下鼠标右键, 在弹出的菜单中选择<Add Behavior Tree>即可.
方法B在工具栏区域 - 点击加号按钮也可以立即创建一个Behavior Tree组件. 
Behavior Tree组件创建完毕后, 你就可以开是给它添加任务节点了. 创建任务节点也有两种不同的方法
方法A在图表区域 - 按下鼠标右键, 在弹出的菜单中选择<Add Task>分栏. 然后根据自己的需求选择需要创建的节点类型. 
方法B在属性区域 - 选择<Tasks>分栏, 在这里选择要添加的任务节点类型. 

# 一个任务节点添加的样例 


: (见参考图03) 我们在行为树中创建了一枚Sequence节点.(也叫顺序节点).可以看到除了你刚添加的顺序节点之外,上方还自动增加了一个名为Entry的入口节点. 

你要记住,Entry节点,是整个行为树的根节点,它是在首次创建节点时自动生成的.我们还可以看到在刚刚添加的Sequence顺序节点上有一个红色报错.这是因为它的运作需要子节点的支持,等你为它添加子节点后,这个错误就会自然的消失了.不必在意.

接下来我在继续添加一些新的节点,让我们可以更好的观察行为树的视觉表现.


这里要额外说明一下,Action A-E 这5个行为节点是怎么来的. 你可能已经注意到了,在你的添加任务节点菜单中,并没有这几个节点选项. 因为这些节点是需要用户手动创建的. 


# 那么如何创建自己的行为节点(Action节点)呢? 

接下来我用一小段代码来演示如何创建一个名为 ActionA的"行为"节点. 

// 必须引入的命名空间
using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

// ActionA行为节点 <必须继承自Action类>
public class ActionA : Action
{

} // End

当你完成该节点的类文件创建后,再次回到Behavior Tree的窗口进行Add Task/Actions类型的节点添加时,就可以看到这个名为ActionA的节点了. ^_^ 

现在所有节点已经创建好了,接下来你要把每个节点像(参考图:05)那样连接到一起.


# 那么如何将节点连接到一起呢? 

你应该会注意到,每个节点图标的上下端都至少有一个小型的突起图案(参考图:06). 这就是操作连接节点的地方,你只需要将鼠标放在一个节点的突起图案上,然后按住鼠标左键进行拖拽,就会出现一条白色的连接线,我们将这条线放置在其他节点的突起图案区域,就会形成节点连接. 非常容易. 

当然如果产生了错误需要修改/删除,我们只需要左键单击对应的连接线然后按下Delete键,就可以实现节点线的清除了. 



# 节点的执行顺序是怎样的呢?

Behavior Designer 的节点执行顺序为由左至右,并且深度优先. 仅看字面意思比较难以理解.我们以参考图:05为例. 描述一下它的节点执行顺序. 

Entry > SequenceA > SelectorA > SequenceB > ActionA > ActionB > ActionC > SelectorB > ActionD > ActionE

了解节点的执行顺序非常重要. 这是行为树执行逻辑的根本所在. 童鞋们务必牢记. 


* 到这里我们已经创建了一棵基础行为树了. 接下来让我们来修改其中一个节点的属性值. 

# 如何修改任务节点的属性值呢?

我们将以ActionC行为节点为例,进行下面的讲解. 那么首先选中ActionC节点. 然后看向其左侧的属性区域面板. 并确保打开Inspector分栏.  (见参考图:07) 我们可以在这里修改节点名称, 修改Instant属性(该属性的意义在后面解释,不必着急.) 添加Comment说明.  


除此之外,我们可以在这里修改该行为类所包含的一切Public类型的变量. 以上图为例,我们看到ActionC行为节点类包含了一个名为Rotation Speed的变量值, 在这里我可以随意的修改该值,比如让它等于5. 这样的值都可以在行为树中参与逻辑交互. 

当然了,你的ActionC类文件肯定没有这个RotationSpeed属性. 去类文件中声名一下就好了. 可以参考下面代码. 

using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class ActionC : Action
{
    public float RotationSpeed = 0;
}


# 介绍一下工具栏区域(Operations toolbar) 

具栏区域主要用于进行 - 选择/添加/删除/保存行为树,等功能. 以下图为例,我们对应数字挨个介绍一下它们的对应功能.


1这是一个行为树导航按钮,通过点击左右两枚箭头图标,可以在所有已经打开的行为树文件中进行切换显示. 
2这是一个下拉框按钮,点击后会展示出所有该项目拥有的行为树文件,不只是当前场景中的,包括那些导出为prefab或者assets格式的外部行为树文件. 只要在你项目中,它们就会被搜索并展示出来. 
3这个下拉框按钮,跟2是完全一样的功能.但是会显示出当前选择的行为树所绑定的对象的名字
4这里会列出所有绑定在<3>中所选择的对象上的行为树文件. 由此可知一个对象可同时拥有多套行为树加持. 
5减号按钮可以用来删除当前所选择的行为树文件
6加号按钮可以用来快捷的创建一个行为树组件到当前选择的对象上. 
7锁定按钮可以保持当前行为树窗口的选中效果. 避免你在接下来的选择操作中对其产生影响. 
8

保存按钮可以将你当前的行为树文件保存并导出为外部行为树文件. 支持保存为二进制格式(.asset)或者Json格式(.prefab)

在新版本中,Save按钮消失了,改成Export按钮了.但功能是一样的,不必疑惑. 

9Preferences插件功能偏好设定按钮. 在这里可以调节一些插件的功能属性. 比如设定行为树的保存格式.


# 好了.到这里我们已经了解了以下内容.

  1. Behavior Designer 是一款什么样的插件.
  2. 它的界面构成是怎样的.
  3. 如何创建行为树组件以及添加任务节点
  4. 如何用代码自定义属于自己的节点类型
  5. 如何连接节点/删除节点连接
  6. 行为树的节点执行顺序是怎样的
  7. 如何修改任务节点的属性值
  8. 工具栏的各个按钮的功能是什么

关于Behavior Designer 中文教程 的第一章节到这里就结束了. 希望可以帮到大家. *_*


没有更多推荐了,返回首页