unity note:
游戏和三维互动内容开发工具,专业游戏引擎
游戏引擎: 一款游戏最核心的代码
包含: 渲染引擎,物理引擎、碰撞检测,音效、脚本引擎、动画系统
人工智能、网络引擎、场景管理。
游戏公司分工
策划、美工、软件工程师、测试工程师、客服
首月:
day1:
环境搭建、C#语言基础、Unity API、物理引擎1、3D数学基础、UGUI
Inspector: 检查监视面板
Transform -> Position 坐标
3d->cube 顶点吸附 按V键
Pivot - Center 设计坐标-中心坐标
Global - Local 世界坐标-自身坐标
视图: IOS(2D) - Persp(透视)3D
世界坐标: 场景坐标
本地坐标: 物体自身坐标
场景: 一组相关联的游戏对象的集合。
游戏对象: GameObject ->容器—>组件->{
功能的模块{
Transform变换组件、
MeshFilter网格过滤器、
Mesh Render 网格渲染器}
}
day02:
1.材质material: 色彩,文理、光滑度、透明度、反射率、折射率、发光度。
png使用较多,psd(photoshop文件)
材料模式(Render mode):
opaque(不透明的)
cutout(去掉透明通道)->剪裁
Fade(渐变)
Transparent(透明)要设透明度
shader 着色器: 渲染管线,控制GPU
Shader->Material{Texture(材质),Color}->Object
2. 摄像机camera:在一个场景中出现数量不限,显示方法-》视锥
组件:
Transoform变换组件
camera
flare Layer 耀斑层
audio listener 音频监听器:接收场景输入的音频
camera-> Clear Flags: 处理屏幕空白部分
-> 天空盒 - 模拟天空材质
-> 6面组成,程序的。
material-> shader->skybox
->DepthOnly ->去除白色部分
使用-> a. 摄像机添加-addcomponent-> skybox
b. 光照窗口(物体可反射颜色,常用) Windows->Lighting->skybox
-> Procedural 有太阳、只有2种(天、地)。
Culling Mask 去除层。 将某层元素去除。该层不渲染
Projection(投射) Perspective(透视的,三维的视角)/orthographic(直角的)-> 去除纵深
Clipping Plane : Far设置视距 Near: 多远才能看到
Viewport Rect: X,Y,W,H : XY视窗坐标,WH设置视窗大小
可实现分屏。
Depth map的depth大于主摄像机,map摄像机才能显示
(要在前面的深度值高)
3.InstantOc 渲染管线: 图形数据在GPU上经过运算最后输出到屏幕的过程
游戏-> 图形API -> GPU -> 顶点处理->…
Draw Call : 准备数据并通知GPU的过程,每帧调用显卡渲染物体的次数
= Batches
↓顶点处理: 接收模型顶点数据。顶点转换
↓图元装配: 组装面
↓光栅化: 计算三角面上的像素
↓像素处理: 对每个像素区进行着色
↓缓存: 帧缓存/深度缓存z-buffer
Occlusion culling: 遮挡剔除@@@ 将摄像机视角内看不到的物体剔除,提升渲染性能
https://blog.csdn.net/qq_33537945/article/details/79159070
步骤-> 将参与剔除的物体标记遮挡静态(Occluder Static/Occludee Static)
Windows-> OcclusionCulling 单击下方Bake
API
IOC layer 层
IOC Tag 层标签
simples 采样率一般在150-500之间
Rays FOV 和摄像机field of view 一致
View Distance 参考摄像机Far
Hide Delay 当前物体被剔除时延迟的帧数。一般50-100之间。
PreCull Cheack : 检查采集信息
Realtime Shadows : 实时阴影
LOD: Layer of detail 实现分级渲染
步骤->多精度模型加入父类object,父类object添加LOD Group
优点: 节省GPU
缺点: 占内存
物体添加-> Box Collider 添加碰撞箱
<8,10,12章> 《3D数学基础》15章 :图形数学
day03
阴影: Light-> Shadow Type:有无阴影,软硬阴影
strength:强度
Receive Shadow:接收阴影
mesh Render - >Cast shadow-> 只阴影,无阴影,双面阴影
阴影剔除
:节省资源
edit-> project setting -> shadow->distance
环境光: 作用于场景内所有物体的光照。
Lighting -> Environment Lighting -> Ambient
source: 环境光来源
Intensitive: 强度
反射光: 根据天空盒计算的作用于
Lighting -> Environment Lighting ->reflection
间接光照
Light-> Bounce Intensity
实时GI
运行时改光源,运行时产生间接光。开发期间做了预计算
Edit -> Perferences->GI Cache
烘焙GI
https://blog.csdn.net/leeby100/article/details/99827850
将光线效果预渲染成贴图再作用到物体上模拟光影
Light-> Baking
Baked GI
游戏对象设置为Lightmaping Static。
设置Light Baking 属性
7.光源侦测
物体感受bake的光源。 小球储存光影信息。
Creat-> Light Probe Group
8。声源: 支持的音频格式:
mp3,ogg,wav,aif,mod,it,s3m,xm
声音分为2D,3D
3D:空间感,2D背景音乐
Audio Listener: 声音监视器
Audio Souce: 声音源
Audio->Audio Source
3D Sound Setting
rolloff 衰减方式
Audio listener Audio
day03:
.NET dotnet 新一代多语言开发平台。
c# csharp
Mono 支持在其它操作系统下开发.NET程序框架
Unity 借助Mono实现跨平台
day04:
P24
C#编程
类->方法
->属性
命名空间-> 类-> {方法,属性}
using … 引入命名空间
CTRL+K+F -> 自动对齐
CTRL+K+C -> 注释选中的代码
CTRL+K+U -> 取消注释
整数
sbyte 1个字节有符号8位
byte 1个字节无符号8位
short 2个字节有符号16位
ushort 2个字节无符号16位
int 4字节
uint 4字节
long 8字节
ulong 8字节
浮点数
float 4字节单精度浮点
double 8字节双精度浮点型
!decimal 128字节,精度28-29位、适用于财务和货币计算
C#的float赋值必须加f
decimal赋值必须加m
float a = 0.3f;
decimal a = 2.9m; ->2.9的意思
->
浮点型运算会有舍入误差
字符
char 2字节,使用单引号
string 字符串,双引号
布尔型
bool 1字节,1,0
调试: 打断点-> F5调试-> F11执行下一行
Shift+F5 结束调试
直接输出结果 ctrl+F5
强制类型转换
字符串等转整数
1.(int)… (这个只适用于浮点数转换)
2.Convert.ToInt32(string)
3.int.Parse(string)或 int.TryParse(String,out int)
输出流:例子
Console.WriteLine(“枪名:”+gunName+ “\t弹匣容量:”+ magazineNum + “\t弹匣内子弹数量:” + currentMagazineNum + “\t当前子弹数量:” + currentbulletNum);
格式占位符
string a = string.Format("{0},{1}",a,b);
{0:d2} 5 -> 05 15-> 15 2位显示
{0:f1} 1.23-> 1.2 1.26-> 1.3 1位小数
" \ -> 转义符
《c#入门经典》 变量与变量表达式
《代码大全2》 10章,11章
day05
dotnet
Mono
CLS : 公共语言规范 定义.net平台上运行的语言规范
CLR :公共语言运行库。 exe->CLR编译(汇编)-> 机器码
.cs文件-> CLS -> .exe -> CLR -> 010100011
运算符:
赋值运算符: =
运算符: + - * / %(取模)
比较运算 < > <= >= == !=
逻辑运算 && || !
快捷运算符 += *= /= %=
一元运算符 ++ –
二元运算符 = a + b
三元运算符 string a = 1>2?“3”:“4”
优先级 …
string转换为 其它类型
int.Parse
…
Tostring : 任意类型转换为string
int number = 18;
string str = number.Tostring();
隐式转换, 自动转换;
byte b3 = 100;
int i3 = b3; -> TRUE
4.显式转换 : 强制转换;
int i3 = 100;
byte b3 = (byte)i3;
判断一个数奇偶性: bool r3 = a1%2 == 0;
定义字符数组
char[] a = new char[4];
byte b=250;
b+= 7; 快捷运算符,可实现,不会自动提升类型
b = (byte) b+7; 会转换为int
语句: 选择语句、循环语句、
if(…)
{
…
}
else if(…)
{
…
}
else
{
…
}
短路逻辑: && 和||
与出现0,和或出现1 都不再执行后项
day06 、
!!!
C#在函数内定义的变量为局部变量,循环结束后失效
循环语句:
for(; {…continue …break…}
while(){} …
do{} while()
注释// /**/
!创建随机数
Random random = new Random();
产生一个随机数
int number = random.Next(1,101);
跳转语句: continue、break、return goto
函数(方法):
[访问修饰符][可选修饰符]返回类型 方法名称(参数列表)
{
…
}
private static void Fun1(){} //函数首字母大写
返回类型: int double short float string decimal void
day 07:
函数重载,根据参数数量判断。
名称相同,返回值相同。
递归: …函数自调用
优点: 复杂简单化
缺点: 性能差,难以理解
取绝对值: ststem.math.abs()
数组:
int[] a;
a = new int[6];
-> int[] a = new a[3]{1,2,3};
初始化+赋值
a.Length
Array.Clear(a) 清除元素值
Array.Copy(a)
a.CopyTo
a.Clone
Array.IndexOf(a,num) Array.LastIndexOf(a,num)
Array.Sort(a)
Array.Reverse(a)
static float[] Createasdas(){
return scoreArray; -> 返回数组
}
day 08:
//从头到尾
foreach(int var in arr){
}
二维数组
创建二维数组:
int[,] a = new int[3,5]={0}; //初始化后全为0
2048
Arr.Length -> 获得总长度
Arr.GetLength(0或1) ->获得行数量,列数
day 09:
交错数组:每一个元素中间可以套数组。
4元素交错数组
int[][] array02;
array02 = new int[3][];
array02[0] = new int[3];
array02[1] = new int[5];
array02[2] = new int[2];
array02: [
000
00000
00
]
赋值-> array02[0][0] = 99;
foreach(int[] array in array02){
foreach(int i in array){
…
}
}
!!参数数组
!!static int Add(params int[] arr) -> 传参数数组
1.可传递数组、传递一组数据类型相同的变量集合
2.可以不传递参数
params int[] arr; 参数数组
可以实现 -> Add(1,23,4,6,8,3,4);
不需要输入数组也可进入函数,自动转换为数组进入函数。
== Add(new int[]{1,23,4,6,8,3,4});
数据类型->
值类型 -> 数据本身,声明在栈中,数据存储在栈中
结构-> int,bool,char
枚举->
引用类型 -> 存储数据的引用(内存地址),声明在栈中,数据存储在堆中
接口->
类 -> string,int[],object
栈区:存储正在执行的方法
堆区:存储引用类型的数据
string s1 = "男"; == new string[]{...};
!引用类型声明赋值 相当于new。
!!!引用参数:
static void Fun2(ref int a){…}
使用 :Fun2(ref a2);
直接对地址能数据进行操作。
!!!输出参数: 按引用传递–传递实参变量的内存地址
static void Fun3(out int a){…}
使用 :Fun3(out a2);
ref out区别:1. 使用ref 前必须对ref赋值
使用out 在函数内部中必须对其赋值,传递之前可不赋值;
使用out实现有多个返回值的函数。
int a;
bool re = int.TryParse(“250”,out a);
垃圾回收器: GC 针对托管堆自动回收释放内存的服务
拆装箱:
int a = 1;
object b = a; //装箱操作: “比较消耗性能”
装箱box: 值类型隐式转换为Object类型。
1.在堆中开辟内存空间。
2.将值类型的数据复制到堆中。
3.返回堆中新分配对象的地址。
int b = (int)o; //拆箱操作:
拆箱unbox: 从object类型到值类型
1.判断是不是装箱时的类型
2.返回已装箱实例中属于原值类型的地址。
形参object,实参值类型,则会发生装箱
通过重载、泛型可避免拆装箱
string str02 = “” + num;
//string str02 = string.Contact(params object);
存在装箱操作
bool r1 = object.ReferenceEquals();
//判断存的是不是同一个
字符串池。
object a = 1;
a = 2.0;
a = “OK”;
a = true; //object每次修改都是开辟新的空间
字符串拼接:! 防止造垃圾。
StringBuilder bulider = new StringBuilder(10);
for (int i = 0;i<10;i++){
bulider.Append(i);
}
string result = bulider.Tostring();
bulider.Insert;
string 方法:
ToArray Insert
Contains ToLower
ToUpper IndexOf
Substring Trim :去除开头的字符串
Split Replaace
Join
day10:
枚举:
定义枚举类型:
enum MoveDirection{
Up = 0,
Down = 1,
Left = 2,
Right = 3
}
简单枚举: 列举某种数据的所有取值
增强可读性,限定取值
枚举的多选 enmu a | b
|按位或运算符。
p91
1.任意多个枚举值做|运算,结果不能和其它枚举值相同
2.定义枚举时,使用[flags],表明可以多选
[Flags]
enum PersonStyle{…}
:定义标准枚举
&按位与运算
|按位或运算
^非
Enum.Parse(typeof(PersonStyle),"…");
!类和对象
创建类;
访问级别 class 类名
{
类成员…
}
通常每个类在一个独立的c#源文件中
创建新的类意味着在当前项目中创建了一个新的数据类型
this.name = name;
类 对象;
对象 = new 类();
成员变量: 类中方法外的变量,具有默认值,默认为null。
访问修饰符
!字段,属性: 字段首字母小写,属性首字母大写
!属性:对字段起保护作用。可实现只读,只写…
public int Age{
get{
return this.age(字段); //获得age字段值返回
}
set{
this.age(字段) = value; //value为获得属性,直接写value
}
}
只读: 只有get 只写:只有set
构造函数:初始化类的数据成员
public 类名(…参数)
{
…
}
对没有构造函数的类会提供一个空构造函数。
构造函数可以重载
如果不希望在类的外部被创建对象,
可以将构造函数私有化。
单例
调用无参数构造函数:继承?
public 类名(…参数):this(…) !调用无参构造,或者this()里面加参数
{
}
构造函数重载+嵌套。
this 关键字:
一个标准类:
class User
{
//字段
private string loginId;
//属性
public string LoginId;
{
get{return this.loginId;}
set{ this.loginId = value;}
}
public string Password{get;set;} //这个是语法糖
//构造函数
public User()
{
}
public User(string id,string password):this(id)
{
}
//方法
public void PrintUser()
{
console.WriteLine(....);
}
}
data.CopyTo;
Ctrl+m 提取函数
day10:
C# 集合
列表集合:
List<数据类型(int,char)>
List list02 = new List();
list02.Add(u1);
list02.Add(new User());
字典集合:
Dictionary<键,值>
Dictionary
Dictionary
dic.Add(“123”,new User(“123”,123));
User lhUser = dic[“lh”];
//prop +tab+tab -> 快速创建属性
!继承:
Class Son:Father
{
…
}
public:公开,都可以用
private:Father的,Son继承了但不能用。
protected级别:Fahter,Son可以用,别人不能用
//父类型的引用指向子类对象
//只能使用父类成员
Father person02 = new Son();
//要使用子类的要强转
Student stu02 = (Student)person02;
//↓如果转换失败就出错。
Teacher teacher02 = person02 as Teacher;
Static:静态
类变量和成员变量的区别。
Static 变量使用类名调用
private static int ascxv;
修饰成员变量: 静态成员变量属于类,实例成员变量属于对象
!静态: static
静态区: 单独存储。
静态构造函数:初始化类的静态数据成员
static Student()
{
…
}
仅在类加载时调用一次
静态方法:只能访问静态成员。
this在静态方法中无效
静态类
使用static修饰的类:
不能实例化、只能包含静态成员。
不能被继承、但静态方法,属性可以被继承
利:单独空间存储、所有对象共享、可以直接被类名调用。
弊:静态方法只能访问静态成员、共享数据被多个对象访问会出现并发。
适用场合:
1.所有对象要共享的数据。
2.没有对象前要访问的成员。
3.工具类适合做静态(常用,不需要过多数据)
(Math.Abs(),…)
结构体:
用于封装小型相关变量的值类型
结构属于值类型,类属于引用类型
struct Direction{
只有两只方法可以赋值:
const(常量)、static
public int RIndex{
get;int;
}
public Direction(int r,int c):this()
{
//如果有自动属性 {get;set;},必须先调用this()
this.RIndex = r;
}
}
结构体自带无参构造函数
2048 核心类
day11: Unity编程
脚本:
.cs的文本文件 类文件
附加在游戏物体上用于定义游戏对象行为指令代码
using 命名空间;
public class 类名:MonoBehaviour
{
void 方法名()
{
…
}
}
!文件名与类名必须一致
!写好的脚本必须附加到物体上才执行
!附加到游戏物体的脚本类必须从MonoBehaviour类继承
编译过程:
源代码–CLS->中间语言(DLL)–(Mono Runtime) -> 机器码
脚本就是一个类
Update() ->0.02会调用一次
!!!!
父类有Update子类不会调用,解决方法->
父类->
protected virtual void Update(){
}
子类->
protected override void Update(){
}
public calss Demo01:MonoBehaviour
{
}
!脚本生命周期(消息、必然事件):
unity脚本从唤醒到销毁的过程。
[SelectionBase] //序列化字段
private int a = 100;
加上标签-> 私有变量显示在编译器中。
[HideInInspector]
public int b;
在编译器中隐藏字段
[Range(0,100)]
public int c;
在编译器中限制c的取值
unity脚本里面看不到属性。
unity中的脚本:
//字段
//方法(函数)
//脚本中不写构造函数
/********初始阶段:/
Awake: //执行时机: 创建游戏对象 立即执行
作用:初始化
Start: //执行时机: 游戏对象创建->脚本启用->执行
作用:初始化,充当构造函数
OnEnable: //执行时机:当脚本对象启用时调用
private void Awake()
{
Debug.log(“Awake” + Time.time + “–”+this.name);
}
private void Start()
{
Debug.log(“Start” + Time.time + “–” + this.name);
}
private void OnEnable(){…}
/***物理阶段/
FixedUpdate 固定更新:
//执行时机,每固定时间(0.02s)执行一次
脚本启用后:固定时间被调用:适合对对象做物理操作
设置更新频率: “Edit” --> “Project Setting” -->
“Time” --> “Fixed Timestep” 值,默认为0.02s
private void FixedUpdate()
{//渲染时间不固定(渲染质量不同、配置不同)
Debug.Log(Time.time);
}
Update 更新
//执行时机: 渲染帧执行、执行时间不固定
//适合性: 处理游戏逻辑
private void Update()
{
}
LateUpdat 延迟更新:
//防止发生相机先手人后走的情况发生
//执行时机: 在Update被调用后执行,适用于跟随逻辑
private void LateUpdate()
{
}
/输入事件*******/
OnMouseEnter 鼠标移入 需要碰撞器
OnMouseOver 鼠标经过
OnMousExit 鼠标离开
OnMouseDown 鼠标按下
OnMouseUp 鼠标抬起
/场景渲染****/
OnBasecameVisible 当可见
当Mesh Renderer 在任何相机上可见时调用
OnBecameInvisible 当不可见:
当Mesh Renderer 在任何相机上不可见时调用
/结束阶段*****/
OnDisable 当不可用
当对象变为不可用或附属游戏对象非激活状态时调用
OnDestory 当销毁:
当脚本销毁或附属游戏对象被销毁时调用
OnApplicationQuit 当程序结束:
应用程序退出时调用
文档-> 索引 -> monobehaviour
脚本生命周期:
AWake -> OnEnable -> Start
FixedUpdate->>>>>>OnCollision↓
↑ <<<<<< ←(或↓)
yield WaitForEndOfFrame->OnApplicationPause
…
具体详查脚本生命周期图。
https://www.cnblogs.com/android-blogs/p/6148188.html
-> OnDestory->OnApplicationQuit
Unity编辑器:
1.控制台调试 Debug.Log(变量);
print(变量);
定义public变量,在程序运行后在控制台中看
VS调试
//快速监视
//即时窗口
逆向: p118
print继承自monobehaviour
单帧调试 -> 启动调试 运行场景 暂停游戏
加断点 单帧执行 结束调试
!常见API:
Unity核心类图
http://www.ajnote.com/unity-%E6%A0%B8%E5%BF%83%E7%B1%BB%E5%9B%BE/
Component:
{
提供了(在当前物体、后代、先辈)查找组件的功能;
this.transform.position = new vector3(0,0,10);
//改坐标
this.GetComponent().material.color = Color.red;
//改颜色/改材质。。
文档->Function->GetCompent
//获取当前物体所有组件
if (GUILayout.Button(“Components”)) {
var AllComponent = this.GetComponents();
foreach (var item in AllComponent) {
print(item.GetType());
}
所有子物体,包括后代的同一组件
getComponentsInChildren().color = color.red;
不带s,只找自己的组件。
getComponentInChildren()
//往上找所有祖宗
getComponentsInParent<>();
//只找上一代
getComponentInParent<>();
//对比标签是否一样。可以直接==
CompareTag
}
Transform:
{
//通过当前变换组件获得每个子物体的变换组件
foreach (Transform Child in this.transform) {
print(Child.name);
transform.position :在世界坐标系的位置
localPosition: 相对于父物体轴心点的位置
rotation/localrotation
localScale: 相对于父物体的缩放比例
lossyScale: 物体与模型缩放比例,相对于世界(只读)
//父3,自身local:2, lossyScale 为6
方法(函数):
Translate: 移动指定方向的距离。
this.transform.Translate(0,0,1);
//向自身z轴移动一米
this.transform.Translate(0,0,1,Space.World);
//基于世界坐标系移动
Rotate: 转动
//y轴转动10度(沿自身,沿世界)
this.transform.Rotate(0,10,0)
this.transform.Rotate(0,10,0,Space.World)
//沿选定轴旋转旋转
RotateAround
this.transform.RotateAround(Vector3.zero,Vector3.up,1);
//参数:点、轴、角度 例子:绕零点,y轴转1度,通过repetButton实现连续旋转
//获取根物体:
Transform rootTF = this.transform.root;
//获取父物体
Transform parentTF = this.transform.parent;
//改爸爸-> 拿起物体
this.transform.SetParent(Transform x);
//找孩子根据名称
this.transform.Find(子物体名称);
//找孙子
this.transform.Find(子物体名称/子物体名称);
//根据索引找孩子 //解除父子关系 //找兄弟索引 //改变兄弟等级 拖物体归属-> 改变变换组件(Transform) } !GameObject isStatic 方法: !!!!AddComponent{ SetActive -> 设置物体的启用和禁用 //在场景中根据名称查找物体(慎用) //获取所有使用该标签的物体 //获取使用该标签的物体 this.transform.Find("…"); } Object 删除组件 -> destory DestroyImmediate 立即删除,不建议使用 DontDestroyOnLoad 加载新场景时不自动清楚该对象 !//根据类型查找一个对象 Instantiate-> 克隆原始物体并返回克隆物体 } task0809_01 :组件-> Hp day12: LookAt: 注视旋转 !Time类 time: 以秒计算、游戏开始的时间。 !deltaTime: 完成最后一帧的时间。 保证旋转/移动-> 不受渲染影响。 !timescale->时间的倍数->实现倍速,慢动作,暂停 timescale>1 加倍 timescale=0 -> deltaTime会受影响->实现在Update的暂停 fixedDeltaTime; unscaledDeltaTime->不缩放的帧时间。 } 在Start中使用 CancelInvoke(…) //取消重复调用 ///3种方法实现倒计时 Invoke(…); 预制件Prefabs 从Hierarchy拽到Project面板 day13 Animation View 创建动画片段 ->物体添加Animation组件 时间线 1:30 -> 1秒和30帧 默认1s->60帧 OnMouseDown-> … Animation类 } day14 敌人 1.敌人沿指定路线运动 分析-> 每个事情分类 敌人生成器 1.开始时生成指定数量的敌人 分析 脚本1-> 敌人生成器 EnemySpawn 类-> 路线WayLine } Enemymotor LookRotation(); -> 注视旋转 bool Pathfinding(); -> 寻路是否完成,沿路线移动 EmenyStatusInfo maxHp; Damage(); Death(); } EmenyAnimation //动画行为类 } 动画行为类,提供动画行为。 获得动画。 //播放动画 } 敌人AI } private State currentState = State.PathFinding; Update(){ case 攻击: break; } 敌人生成器 //路点是否可用 } EnemySpawn -> 计算所有路线及坐标,生成一个敌人 //需要创建的敌人预制件数组 //最大敌人数量 //开始同时创建敌人数目 //已经创建的敌人数量 public void GenerateEnemy() Instantiate(enemyType[Random.Range(0, enemyType.Length)], line.WayPoints[0], Quaternion.identity); } 路线随机, 计算敌人路线 选择一条可以使用的路线(WayLine-> IsUsable = true) } //获取鼠标输入 Input 一般在Update调用 //获取键盘输入 检测在c按下的前提下按d实现 实现鼠标右键拉近镜头 插值 index = (index+1)% zoomLevel.Lengtn; 枪口火光特效-> 火光对象loop(启用->禁用) //InputManager-> 实现用户自定义按键 bool result = input.GetButton(“虚拟轴名”) !!! 3D数学 vector3 a; 求模 求方向 ->向量单位化(归一化,标准化) 向量计算 } } tranform.TransformPoint(Vector3 a) } 点叉乘 Vector3.Dot ->点乘(内积) 通过两向量可以求他们的夹角 在实际运用中通常会把两个向量先标准化 float dot = Vector2.Dot(a.position.normalized,b.position.normalized) Vector3.Corss ->叉乘 几何意义-> 两个向量所组成面的垂直向量 Vector3 cross = Vector3.Cross(a.position,b.position) 通过叉乘可以判断两个物体的相对位置方向 AxB BxA 复合左手定则 if(cross.y<0) //逆时针超过了180° 模长为两向量模长乘积再乘夹角正弦 mathf.Asin(cross.magnitude)*mathf.Rad2Deg; this.tranform.forward ->获取物体正前方向量 } 欧拉角 } } Vector3 magnitude 静态函数 } } 1.欧拉角-> 四元数 2.四元数 -> 欧拉角 3.轴/角旋转 4.注视旋转 5.插值旋转(慢慢转方向) x += Time.deltaTime/duration; 6.匀速旋转-> Quaternion.RotateTowards(); 7.同一性(和世界坐标一致) 8.x轴注视旋转(2D游戏中非Z轴注视旋转) //x轴正方向 -> 注视目标 !!! } } day16 world Space Local Space Screen Space } Viewport Space -> 视口(摄像机)坐标系,原点在左下 物理引擎 Rigidbody ->刚体 Constraints: 约束 对刚体运动的约束 UseGravity -> 重力 不带刚体的对象默认无法运动,绝对质量 Is Kinematic-> 运动学刚体->也拥有绝对质量 collider -> 碰撞器 物理材质 } 碰撞条件 碰撞三阶段 } 触发器-> 带有碰撞器组件且Is Trigger { } 子弹检测不到碰撞 RaycastHit hit; !!! 返回值为bool . hit中包含-> if((this.tranform.position - targetPos).sqrMagnitude<0.1f) } } } } public class SingleGun:Gun } } class Bullet:monobehaviour } public class PlayerBullet:Bullet } public class EnemyBullet:Bullet } Trail Renderer -> 尾痕追踪 PlayQueued() 配置文件? 对象池? } } day18 HTC VIVE Steam { 人眼接受刷新率 -> 70以上 控制器 SteamVR包 } GetPress GetTough GetAxis -> (0,1) TriggerHapticPulse: 振动,力度为1-4000 GetHairTrigger ->轻微碰到扳机 …-P207 -> VR方面的介绍 创建玩家预制件 P200 19:06 Package 2 VRTK -> P202 加命名空间! 特殊目录 Plugins -> 插件目录 会提前编译 事件-> 当满足某种条件时自动调用的方法 !! Brizer Pointer -> } } day19 P208 权限布局系统 强大的事件机制 减少GPU负担 淡化了Atlas -直接使用Sprite Packer 没有Tween组件 -> Itween / DoTween Canvas 画布 画布内 -> 后面的盖前面的 画布模式-> 覆盖模型性能优于摄像机模式 设置UICamera-> 深度值调高 主摄像机-> 不看UI 另一种做法 -> 把UI摄像机丢到很远的地方去 3D物体盖住UI-> World Space Rect Transform Mouth 02 持久化数据 换装案例 协程/多线程 文件IO 寻路系统(网格寻路) 射线 Mecanim(animator) 物理引擎(关节.布料) 美术规范(能够与美工沟通交流) Shader(Shaderlab基本结构 常用功能 写常用Shader) NGUI Unity2D(工作流程) 手机触发方式以及AR } 容纳多个数据、大小可变、空间不一定连续 集合两大体系-> 命名空间: 非泛型缺点 列表 (非泛型) ArrayList 字典 (非泛型) Hashtable 作业1-> 字典-> 无序输出 嵌套字典 -> 栈 Stack(后进先出) Stack } } 持久化数据 } } tranform.GetChild(i).GetComponent().onClick.Add( 获取当前按下按钮的名字->添加事件 } private void GetButtonName() } 角色换装实例 按钮(分配任务) -> 业务(资源使用) } Replace(“xxx”,""); //将字符串的某一串字删掉 } 灵活,可以有多个入口和出口 适合用于合作式任务,迭代器,无限列表 开始一个协程时,执行从被调用的起始处开始,然后 协程是单线程的。 实现 private IEnumerator Start() 自己定义的协程要使用StartCoroutine开启 使用-> WWW 请求结束后使用 url = " 一个网址 " //下载完成、画图 StartCoroutine(…) … 开启协程 携程调用参数 不用写类型 //!!协程嵌套 } 协程的停止-> 多个协程可以同时运行 协程可以嵌套 多个脚本访问同意协程,可以定义静态协程 !协程不是多线程,运行在同一线程中 可以创建一个随时间进行的协程来进行大量运算 IEnumerator 不能带ref或out参数 } 文件对应类 System.IO DriveInfo :有关驱动器信息的类 File类 } FileInfo类 有关文件整体操作和读写 Directory类 Delete 删除文件夹 Move 移动文件夹->不能跨驱动器 GetFiles 取得文件夹下所有文件名 GetDirectoies 取得所有子文件夹名 Exists 有无此文件夹 } DirectoryInfo类 ->区别:所有成员为实例成员 Path类 GetFileNameWithOutExtension 取得文件名,不要扩展名 GetDiectoryName 取得路径目录名称 Combine 将多个字符串合并成为一个路径 } 数据流类 IO Stream } StreamReader: 流内容读取器 !!使用流前需要创建流对象 } } !!!路径 private void Start() Directory.Delete(path,true) //DirectoryInfo ->实例成员 路径 -> @"…" !!!StreamingAssets文件夹-> //读取配置文件路径(StreamingAssets文件夹) string ConfigPath = string[] file02 = File.ReadLines(ConfigPath); string a = “”; //相当于python 里面str.strip() 字符数组转字典 private void BuildDic(string[] lines) } 已经烘焙了之后可以隐藏/删除物体 //搭桥 Nav Mesh Agent } } Navigation->Bake-> } GetComponent().areaMask = 2^n; (复选枚举) } 注意问题-> } 常见寻路插件 } day22: 一个寻路的脚本 } public Camera mapCamera; canv.gameObject.setActive(true); camera.main.ScreenPointToRay() 搬东西 public Transform PlayerTF; public Transform CenterPointTF; //玩家操作的准心中点 private RaycastHit hit; //击中对象 //public Camera PlayerCamera; //玩家摄像机 public LayerMask layer; private bool UporDownFlag = false; //ture拿起 Down放下 private void Update() } private void PickUpfun() } 添加父子关系 取消父子关系 画出射线-> Component -> LineRander 纹理偏移-> Material.mainTextureOffset line.enable = …; line.SetPosition(0,起始位置) !!射线 } } day24 针对人形提供了特殊的工作流-> 动画重定向->方便地把动画从一个角色模型应用到另一个角色模型上 Animator视窗 { } 预制件-> Rig -> Animator Controller-> Retargeting Set Default State ->设置默认状态 MakeTransition -> 设置动画衔接 添加动画条件-> } SetLayerWeight(1,1) ->第一层权重为1 动画事件-> 需要的动画-> Event -> Add Event 死亡-> 与任意状态 AnyState连线 Animator Overide Controller 判断动画是否在播放-> } day26 物理引擎 关节{ 铰链关节 -> Physics-> Hinge Joint } 布料引擎 Component->Cloth 布料约束 表面嵌入 -> Surface Penetration } Stretchomg Stoffness 拉扯硬度 //模拟风 //布料运动时,各加速度影响比例 //碰撞-> 只支撑胶囊和球形碰撞 } Play day27 1.开始界面-> 人物换装 2.随机3个诞生点,一共生成3波怪(每波2-3个) 3.怪头顶有血条,随波数增加怪物难度越来越大 4.角色与怪物之间的动画互动(Mecanim动画系统) 5.敌人,角色血条效果 6.打敌人,随机掉装备,可实现加血等 7.打完后胜利, { 通过脚本添加动画事件 //现在在播放的动画组 //动画播放5s添加事件 //当前动画这 } day28 1.配环境-> b.调自动保存 -> Customize->Preference c.max点大小设置->右键点击物体-> 转换为->可编辑多边形 Alt + 中间旋转 F3 只显示线 X 显示/隐藏坐标位置 Alt + X 透明显示 顶视窗,前视窗,左视窗 步骤-> 1.归原点 加线-> 要加的线连接到的所有线都要加 挤出-> Extured 补面-> cap 文档->3dmax->autoback->查看前面做的文件 吸附命令-> 导出文件-> Export-> FBX格式 unity中pivot 和Gobal Center/Global:显示物体的坐标。Global为所选中游戏对象使用世界坐标;Local为该游戏对象使用自身坐标 镜像-> 打组-> Group ->相当于添加父物体 合并-> Attach Attach List ->相当于变为一个物体 贴图 UV展开 各个位置贴图展开(得到看起来很奇怪的texture图) } } day29 模型歪了-> 重置变换-> 转换为可编辑多边形 导出UV图 PS里面打开->放图 P312 PS里面 放图-> 新材质球->Bitmap(位图)双击-> 防止拉伸->复制 (长按Alt) 矩形框选->选中贴图中某一部分-> 防止拉伸 镜像贴图-> 在UV里面改 (挺好玩的) !可以将背景改成其它的->右上角->Pick Texture !!连贯的部分要缝合!! Ps操作 } max中导出-> psd格式(不是性能最好的格式) PS中-> 文件-> 存储为->png 分离物体 保存-导入unity 模型设计全过程-> } 先转可编辑多边形-> 关联镜像 -> 镜像 Instance 创建骨骼-> Biped 模型优化->模型面数,贴图数量,模型大小 录制动画->自动关键点-> 录制动画-> 时间配置(设置播放速度) 骨骼上下移动->运动面板-> 动作复制-> 关键帧选中全体,按Shift 动画混合-> 先选择骨骼再到运动面板-> 混合器 右键->复制到骨骼上Copy to Biped-> } day30 } unity -> 切割动画 unity3d 美术制作规范 } } day31 P330 {Shader glass 玻璃 边缘高光(Rim power/Rim color) 法线贴图(Normal map)->表现模型凹凸效果 Unity shader package包 改变shader !!Shader编程 shader编程种类: HLSL: 主要用于Direct3D 。 Windows平台 unity3d里CG输出: ?缓存-> } day32 NGUI P341 -P367 } day33 Unity2D P368 动画切割-> 精灵切割-> 选中所有切割动画拖到Scene animation-> 改变动画贴图,动画办法速度(simples) 改变图片大小-> PS-> 新建需要大小的画布-> 拖入图片,缩放 unity中的画布背景-> 背景设置为-1 2D碰撞 -> PolyGon Collider !!!使用2D碰撞器触发器 P367 unity2D 碰撞效应 Component-> Buoyancy Effect2D 浮力效应 Point Effect2D 点效应 } Plane Effect2D 平台效应 } Surface Effector 表面效应: } !!!2D项目需求-> } } day34 安卓发布/平台发布 2.指定Android SDK路径 3.Edit->Preference-> External Tool -> unity中选择发布平台 4.选择PlayerSetting 5.设置公司名,游戏图标 6.Build生成APK格式 EasyTouch Input.GetTouch() } day35 复习 P391 碰撞器 } 触发器 } 物理引擎(关节 布料) 美术规范 Shader(Shaderlab 基本结构 NGUI unity2D IOS发布 2D手机项目 } day36 面向对象 P392 需求调研-> 需求细化 } 运动系统/技能系统/AI系统 OOP/Framework/DesignPattern 设计模式 C#高级语言 P402 类、对象 封装 } } 代码复用: 两种方式-> 组合 - 继承 复杂类型字段不在unity属性窗口赋值 p412 注册事件-> #输入参数-> 委托。后面会介绍 private void OnEnable() 移动-> //转向 //向目标运动 } class CharacterMotor private void Start() public void Move(float x,float z) } 继承-> 优点-> !继承特点-> 1.构造方法不会继承给子类 !!!4.当子类创建对象时,默认调用父类的无参构造方法, 用继承的方法 能放在数据类型 (类) 能放数据类型的成员 (字段,方法) 未声明的类的默认访问修饰符为 类中的成员默认访问修饰符 保护类的后代调用后代写法有限制,并不能随便使用 base的作用-> 调用父的构造方法和父的非私有成员 继承组合判断 is/has ? switch (a): case 1: 调用端会单独放在类中 子调父 -> 容易 } } day38 多态polymorphic p418 !体现->子类对象可以通过父类型引用, 行为的多态体现-> !在共性的基础上->体现类型和行为的个性化。 多态-> 复用 选择性要 选择性复用 { 多态实现-> 方法隐藏-> 方法重写-> } day39 抽象类抽象方法接口 } 抽象方法 } 抽象方法和虚方法的比较: !接口 P437 一个例子 } class Shape interface IAreaCalculate } class Rectangle : Shape,IAreaCalculate } class Triangle : Shape, IAreaCalculate } } } !!! 数据类型 标识 可以被继承 可以被实例化 比较 比较 方法类型 标识 调用 比较 能加static吗 能包含在结构中吗 Framework常用接口 Student zs = new Student{Name=“zs”,Age = 20,Tall = 200}; class Student:IComparable public int CompareTo(object obj) //IComparer需要引入System.collection命名空间 自定义对象的比较 写法2 zs.CompareTo(ls); //默认比较年龄 泛化特征-> } day40 委托与事件 P447 delegate : 当调用委托时就调用了这个方法 !语法: 步骤1.定义委托 delegate 返回值 方法签名 步骤2. 创建实例 实例化委托:为委托指定{绑定}方法,只指定方法名 步骤3. 调用委托【和调方法相似,有参数就写参数,有返回值可以返回】 委托调用方式 !委托的几种写法: public delegate Person PersonHandler(List pList, int Id); class Client{ } public class Person } !委托的作用 用途 P452 2.实现回调,实现的方式比接口更灵活 3.通过委托实现异步调用 P459 { class Client } class A } } 应用: !!!事件 P456 } 猫和老鼠: namespace Homework5 public delegate void CatHandler(); //声明猫的委托句柄 class Client } class Cat } class Mouse } class Houser } } } day41 泛型 p463 用于设计一些通用(基于不同类型的复用)的算法 .NET Framework 1.类型参数: 声明泛型的标记,代表任何数据类型 2.泛型 :使用类型参数定义的数据类型或方法 3.语法: MyCollection -> 泛型类 example:-> //使用泛型接口性能更高更安全 public int Age; public int CompareTo(Student other) } } } } 列表 List ArrayList 字典 Dictionary 技能系统设计与实现 制作通用工具类 引入委托: public static void Sort(T[] Array, CompareHandler compare,int Mode = 0) public static void CompareAndSwap(ref T a, ref T b, CompareHandler compare) b. 对象池: P481 { //创建池,双层 /// } /// } /// } /// } /// /// /// private IEnumerator RetrieveDelayCoroutine(GameObject Go, float delay) } } } 单例游戏对象池 创建单例对象池 public class TransformHelper : MonoBehaviour static public Transform FindChild(string name,Transform tf) } static public Transform FindChild(Transform tf, FindHandler findMethod) } static public void LookAtTarget(Vector3 target,Transform transform,float rotateSpeed) } } } day43 技能系统 P494 技能系统框架图 攻击方挂技能管理组件,管理技能数据对象 释放器组件 动态创建技能预制件对象,执行技能算法。 技能数据类图: { } } !在释放器中Skill属性的set中对所有要初始化的字段初始化 private SkillData my_skillData; 通过父类(接口)来实例化子类,调用父类(接口)方法 技能释放器-{近战释放器,远程释放器} 攻击接口-> 圆形扇形 自身影响接口-> 减蓝功能 目标影响接口-> 减血 } day44 事件设计模式-> (观察者模式)。 动画组件。 AnimationEventBehaviour P518 List a a.AddRange -> 一次加入多个元素。 ARPG 2.6 完成 技能系统tree -> } day45 P526 手动生成 使用配置文件-> 资源管理类。 流程-> 加载预制件 } using UnityEditor 程序集反射:P528 程序集定义 应用程序编译后的结果 : exe dll 控制台-> exe 有入口 P529 项目中添加引用。 跨项目调用-> 静态程序集-> 程序集用途: 1》发布部署。 混淆器(加密-> 阻止反编译) 反射-> 获取程序集中信息的技术 提供了描述程序集、模块和类型的对象。 使用反射动态创建对象的实例从现有对象中获取类型并调用其方法。 反射的作用-> 反射常用类 取得数据类型Type 对象的方法:-> Type.GetType(“类型全名”) obj.GetType(); typeof(); Assembly.Load(“XXX”).GetType(“名字”); Type类常用Get系列方法 Is系列属性 MethodInfo(方法) 重要方法:Invoke PropertyInfo(属性) 重要方法:SetValue GetValue FieldInfo (字段) 重要方法:SetValue GetValue ConstructInfo(构造方法) 重要方法:Invoke } 练习:使用反射技术,反射string成员有多少个(166个)。 动态创建对象: //1.得到Type类数据类型的对象 //2.动态【使用反射技术】创建对象//或动态调用方法 //动态【使用反射技术】调用方法 //使用反射技术修改字段/属性 任务2将技能释放器工厂使用反射技术创建。 //实例化类类型为对象。 public static IAttackSelector CreateAttackSelector(SkillData skillData) } } socket编程步骤 网络如何通讯。 TCP Transmission Control Protocol 传输控制协议 UDP User Data Protocol 用户数据报文协议 FTP File Transfer Protocol 文件传输协议 网络上传输大文件 smTP simple mail transmission protocal 简单邮件协议 传输速度 http最慢 tcp udp最快 底层协议-> TCP,UDP } } 组网-> 将许多设备连接成一个网络以共享资源。 新建空白解决方案-> NetCodeDemo-> 解决方案属性-> 启动项目-> 多项目启动 -> 服务端优先启动。 端口: 系统服务保留了端口1到端口1024的权力。 //Server } //Client } C#【应用程序源代码】 csc 【exe,dll,】 clr{il编译器} -> 二进制代码 托管型代码 托管donet平台 C语言 源代码 -> 编译器【exe,dll,;二进制代码】非托管 直接OS执行 Thread ->多线程类 unity不支持框架多线程访问场景中的物体。 P539 通信写法扩展 三大类: 创建型、结构型、行为型 常见设计模式: 创建型设计模式 23个最经典的设计模式 c. Strategy(策略模式) P563 } } day48 AI P566 FSM (Finite-state machine) 有限状态机 Perception 智能感知 寻路 Steering 自动操控 有限状态机: 有限多个状态在不同的条件下相互转换的图 案例: 增加抽象状态类,条件类。 状态机----------------------状态类--------------------------条件类 状态管理 初始化 初始化 切换状态 删除条件 例子: 状态机(史莱姆状态机) 状态类(史莱姆Idlel类,史莱姆FindEnemy类…) 创建文件夹-> Scripts/AI/FSM-> Common,Condition,States文件夹 配置文件方法: }
int count = this.transform.childCount;
this.transform.Getchild(i)
//i=0;i
DetachChildren
//解除父亲关系
SetParent(null);
GetSiblingIndex
SetAsFirstSibling
SetAsLastSibling
{
ActiveSelf
//只读——> 该对象的局部激活状态
activeInHierarchy
//场景中的游戏对象是否激活?
//父未激活则无效
layer
scene
tag
transform
添加组件的几种方法:
{
1.gameObject.AddComponent<…>();
2.[RequireComponent(typeof(…))]
3.直接添加,拖拽
}
添加组件
GameObject lightGO = new gameObject();
Light light = this.gameObject.AddComponent();
light.color = Color.red;
light.type = LightType.Point;
}
!GameObject.Find(“游戏对象名称”)
FindGameObjectsWithTag;
FindWithTag
{
Destroy
Destroy(gameObject,5)
//5s后销毁对象
!FindObjectOfType<>();
//根据类型查找所有对象
FindObjectsOfType<>();
task0809_02 :按钮对象
{
只有静态变量
!-> △t ->
!渲染量-> 帧数多 1s -> 一帧动作小
! 帧数少 1s -> 一帧动作大
-> 相同距离-> 总速度恒定。
!this.tranform.Rotate(0,1*Time.deltaTime,0);
timescale <1 慢动作
timescale = 0 暂停-> FixedUpdate不再更新
但是Update不受影响(原因-> Update受渲染影响)
->实现游戏暂停时部分物体可以运动。
InvokeRepeating(“Timer”,1,1);
//重复调用(被执行的方法名称,第一次执行时间,每次执行间隔)
///1. Tol = Time.time; Tol >NextTime? NextTime = Time.time +1 ->Update
///2. Tol += Time.deltaTime ?.. Tol = 0 ->Update
///3. InvokeRepeat(“Timer”,1,1); CancelInvoke(…) ->Start
(被执行的方法,开始调用时间)
方法1-> 先做一次
方法2-> 先等
方法3-> 简单循环事件
{
Select : 通过预制件实例选择对应预制件
Revert :重置为初始状态
Apply: 将该实例的修改引用到所有实例中
一种资源类型->可以多次在场景进行实例,提高开发效率
可以实现特殊值,改单个
}
动画
通过动画视图可以直接创建和修改动画片段
Window—Animation
{
方法:
PlayCrossFade -> 淡入淡出 -> 两动画之间过渡
PlayQueued -> 队列播放
属性
!speed 0 -1 1 2 ....
播放速度->可以实现倒放
anim["Door"].speed = 1;
length
time
wrapMode ->播放模式(一次,循环,往复播放,固定点一次。)
(once,loop,ping pong,clamp forever)
{
a.需求->分析->实现
{
2.受击后减血死亡
3.到达终点攻击玩家
4.运动播放运动动画,攻击动画,死亡动画,间隔动画
脚本1-> 敌人马达EnemyMotor -> 移动,旋转,寻路
脚本2-> 敌人状态信息EnemyStatusInfo -> 血量、提供受伤、死亡等功能
脚本3-> 敌人动画EnemyAnimation -> 定义动画,播放动画
脚本4-> 敌人AI EnemyAI,通过判断状态,执行寻路或攻击
}
b.需求->分析->实现
2.为每人随机选择一条可以使用的路线
3.敌人类型
4.玩家打死一个后生成新的,直到生成数量达到上限
创建根路线并添加多条子路线
附加提供生成敌人的功能,附加到根路线提供生成敌人的功能
{
MoveForward();
//transform.lookat
//朝向目标点
//向前移动
}
{
Hp;
//播放死亡动画
//销毁对象
{
string runAnimName;
… attackAnimName;
… idleAnimName;
… runAnimName;
… deathAnimName;
public AnimationAction action;
private void Awake()
{
action = new AnimationAction();
}
public class AnimationAction
{
private Animation anim;
public animationAction(Animation anim)
{
this.anim = anim;
}
public void Play(string animName)
{
anim.CrossFade(animName);
}
EmemyAI
{
public enum State
{
Attack,
PathFinding
判断
执行攻击,
执行寻路
switch(currentState)
{
case State.PathFinding:
//执行寻路
//播放跑步动画
//如果寻路结束切换状态为攻击
//攻击不能一直攻击,攻击一阵之后要播放闲置动画
//
}
}
先创建路线类
public class WayLine
{
//创建路点数组
public Vector3[] WayPoint{get;set;}
public bool IsUsable{get;set;}
{
public GameObject[] enemyType;
public int maxCount = 5;
public int startCount = 2;
private int spawnedCount;
{
//选择一条可以使用的路线
//延迟时间随机//Instantiate(敌人预制件,位置,旋转角度);
int randomIndex = Random.Ranage(0,enemyType.Length)
GameObject AnEenemy =
//这句话创建了一个敌人对象,并赋初点//配置信息,马达
go.getConpent
创建所有路线
随机挑出一条
随机时间随机
day15
{
Input类: 包装了输入功能的类
指定鼠标按钮被按下时返回true
//鼠标按钮被按下
bool result = Input.GetMouseButton(0);
bool result = Input.GetMouseButtonDown(0); 这两个只有一帧返回true
bool result = Input.GetMouseButtonUp(0);
0->左键 1->右键 2->中键
bool result = Input.GetKey(KeyCode.A)
bool result = Input.GetKeyDown(KeyCode.A)
bool result = Input.GetKeyUp(KeyCode.A)
Input.GetKey(KeyCode.C)
&& Input.GetKeyDown(KeyCode.D)
1.按下镜头拉近,再次按下镜头拉远
(设置Camrea的Fov属性)
2.按下鼠标右键逐渐拉近
3. 缩放等级变化
Mathf.Lerp(起点,终点,比例)
虚拟按钮 Vertical->
Edit->ProjectSettings->Input
Name-> 虚拟按键名
Negative Button 负向按钮
Positive Button 正向按钮
Alt Negative Button 负向按钮
Alt Positive Button 正向按钮
(一个名称可以绑定4个按钮)
Gravity 复位速度-> 逐渐过渡,越大复位越快
Sensitivity 灵敏度
Snap: 若启用,轴收到反向输入信号时,立即置0。
Invert: 反向(游戏里面混乱状态方向颠倒实现)
Type 输入来源(KEY,Mouse,)
float value = input.GetAxis(“虚拟轴名”)
float value = input.GetAxisRw(“虚拟轴名”)
GetAxis获得的虚拟轴,如果有变化才有值
没变化就没有值,通过这个来进入if
{
Debug.DrawLine(vector3.point1,vector3.point2);
a.magnitude;
math.sqrt(pow,pow,pow)
Vector3.Distance();
a.normalized;
{
a-b -> b指向a (计算子弹轨迹)
//通过单位化,防止子弹速度因为距离变化
zidan.translate((a-b).normalized);a+b -> a 沿 b 的方向走b但单位
(vector3.translate的实现)
向量和标量的乘除 常量k
a*k
a/k 实现缩放
角度度量
{
角度Degree/弧度radius角度->弧度
a * (mathf.pi/180)
a * mathf.Deg2Rad;
弧度->角度
a * (180/math.f)
a * mathf.Rad2Deg; ->一个常量
三角函数
{
通过三角关系计算距离
Mathf.Sin(float radian);
//Sin,Cos,Tan
//返回角度degeeeMathf.Asin(a/b)
//Asin,Acos,Atan
//返回弧度radius
-> 将相对于自身a向量那么远的点坐标转换为世界坐标并返回
{
Vector3 operator ->转到运算符定义查看操作方法
[x1,y1,z1]丶[x2,y2,z2] = x1x2+y1y2+z1z2
几何意义
a丶b = |a|丶|b|cos;
->cos = (a丶b)/|a|丶|b|
-> a丶b = cos;s
//float angle = mathf.Acos(dot)*Rad2Deg;
->省略可以省性能->直接用弧度来判断
[x1,y1,z1]x[x2,y2,z2]=
行列式写法计算
|i, j ,k|
|x1,y1,z1|
|x2,y2,z2|
-> …i+…j+…k
[y1z2-z1-y2,z1x2-x1z2,x1y2-y1*x2]
符合左手定制
结合点乘就可以通过两个物体的相对位置计算夹角
{
angle = 360-angle;
}
但是叉乘算角度只能算0-90°
|cross| = |a|*|b|*sin
this.tranform.right ->右方,左方加负号
this.tranform.up
{
由三个角度来确定方位(x,y,z)
!!!
X,Z沿自身旋转,
Y沿世界旋转Unity限制了欧拉角范围
x轴取值范围 -> -90 - 90
y,z取值 -> 0-360
万向节死锁-> 当x为-90或90时
物体自身z轴与世界坐标系y轴重合,此时无论沿y还是沿z旋转,将失去一个自由度
此时规定沿Z轴完成X,Y的全部旋转Y轴为0
Vector3 euler = this.transform.eulerAngles;
四元数
{
Quaternion -> 由三维向量(X,Y,Z)和标量(W)组成
旋转轴V,旋转角度θ
x = sin(θ/2)*V.x
y = sin(θ/2)*V.y
y = sin(θ/2)*V.y
w = cos(θ/2)访问四元数
this.tranform.rotation
欧拉角转换为四元数
this.tranform.rotation =
Quaternion.Euler(0,50,0);
this.tranform.Rotate(0,1,0);
四元数左乘向量-> 表示该向量按照四元数表示的角度旋转
Vector3 a = new Vector3(0,0,10);
Vector3 new_a = Quaternion.Euler(0,30,0) * a;
两个四元数相乘-> 基于物体自身的的坐标系的旋转再加上要旋转的角度
实现旋转的叠加,基于自身坐标系做旋转。
Quaternion.Euler(0,30,0) * this.tranform.rotation;
{
right
up
down
forward
normalized
sqrMagnitude -> 返回这个向量的平方
->计算长度
->少一个开方省性能
{
Angle()
ClampMagnitude() -> mathf.min(value(长度),vector3);
Cross() 叉乘
Dot() 点乘
Lerp() 插值
normalize() 单位化自身
OrthoNormalize 使向量规范化并彼此相互垂直?
Project 投影
ProjectOnPlane()
Reflect() 反射->(入射向量,法线向量)
-> 输出反射向量MoveTowards() ->匀速移动到终点
Lerp() ->插值,线快到慢
LerpUnclamped() ->不限制终点(有可能超过)
//实现自然的移动
->public animationCurve curve;
->print float x=0;
public float duration = 1;//持续时间
x += Time.delatTime/duration;
vector3.Lerp(this.tranform.position,
rargepPosition,curve.Evaluate(x));
//超过终点
->vector3.LerpUnclamped(this.tranform.position,
rargepPosition,curve.Evaluate(x));
//Quaternion.Euler( );
Quaternion qt = this.transform.rotation;
vector3 euler = qt.eulerAngles;
this.tranform.rotation = Quaternion.AngleAxis(angle,axis); //沿着某条轴转angle度,返回一个四元数
this.tranform.rotation = Quaternion.LookRotation(方向向量); 注视旋转;
->public animationCurve curve;
->public float x=0;
public float duration = 3;//持续时间
Quaternion qt = Quaternion.LookRotation(tf.position - this.tranform.position);
this.tranform.rotation = Quaternion.Lerp(this.tranform.rotation,dir,x);
Quaternion.identity;
//x轴注视旋转 t1
this.tranform.right = tf.position - this.tranform.position;
//实现物体x轴正方向指向目标
FromToRotation()
//获得某个轴到目标位置的方向
!Quaternion dir = Quaternion.FromToRotation(Vector3.rights,tf.position-this.tranform.position);
this.tranform.rotation = Quaternion.Lerp(this.tranform.rotation,dir,0.1f);
Quaternion.LookRotation()
//旋转后有可能改变z轴方向,默认其为世界正方向
{
unity坐标系自身坐标转世界点
tranform.TransformPoint;
tranform.transformDirection;
tranform.transformVector;
世界坐标转自身点
tranform.InverseTransformPoint;
tranform.InverseTransformDirection;
tranform.InverseTransformVector;
-> Screen.width,Screen.height
-> 可以判断物体是不是在屏幕中。
//屏幕坐标一般只用X,Y轴. Z轴是物体和屏幕的距离
{
//获取屏幕坐标
Vector3 localPoint = Camera.main.WorldToScreenPoint()
//限制物体运动范围在屏幕内
//Screen.width,Screen.height//1. 限制移动
//2.左出右进
左下(0,0),右上(1,1);
{
{
刚体,碰撞器
}
-> mass 质量
-> Drag 阻力 砖头 0.001 羽毛 10
angular Drag 角阻力
Interpolate 插值 -> 缓解刚体运动的抖动
-> 内插值 基于上一帧
-> 外插值 基于下一帧
Collision Detection -> 碰撞检测
Discrete 不连续碰撞检测
Continuous 连续碰撞检测
Continuous Dynamic 连续动态碰撞检测
-> 冻结某个方向的运动,比如磁悬浮运动
Material -> Physic Material->
-> New -> Physics Material
{
Dynamic Friction -> 动态摩擦力
Static Friction -> 静态摩擦力
Bounciness -> 弹性
Friction Combine
Bounce Combine -> 弹力合成方式
{
两者都具有碰撞组件 collider
运动的物体具有刚体组件
}
{
碰撞执行 OnCollisionEnter(Collision a)
与刚体接触每帧执行 OnCollisionStay(Collision a)
停止碰撞时执行 OnCollisionExit(Collision a)输入参数碰撞组件 a为 被this碰撞的物体对象
//!!!输入参数是对象,包含接触点数量
//接触点数组
//和Trigger的区别:对象,获取组件需要getcomponent
ContactPoint cp = a.contacts[0]
-> 获取第一个接触对象
cp.point -> 获取接触点
cp.normal ->接触面法线
!
{
(可以实现攻击范围判别)现象-> 没有碰撞效果
条件
{
两者具有碰撞组件
其中之一有刚体组件
其中之一勾选isTrigger
}
碰撞三阶段
碰撞执行 OnTriggerEnter(Collision a)
与刚体接触每帧执行 OnTriggerStay(Collision a)
停止碰撞时执行 OnTriggerExit(Collision a)//Trigger输入参数为碰撞对象的碰撞器组件!!!!!!
//可以直接使用 a.name。
输入参数碰撞组件 a为 被this碰撞的物体的组件
-> 开始时做射线检测
//重载15
public LayerMask mask;
Physics.Raycast(this.tranform.position,
this.tranform.forward,
out hit,150,mask)
mask -> 选择需要检测的层。
//射线起始位置,方向,返回值out hit(受击信息),最大距离
point->点
normal->接触面法线
collider->接触面碰撞器
{
print(“接触”);
Destroy(this.gameObject);
武器模块
{
1.弹夹有子弹,可以发射,否则等待更换弹夹2.发射子弹时-> 播放音效、动画、显示火花
3.玩家的枪-> 单发或者连发 ,敌人->单发
4.玩家子弹
-> 根据敌人补位减血
-> 子弹飞行到目标点销毁并创建相应特效
5.敌人子弹
-> 击中玩家后减血
-> 子弹飞行到目标点销毁并创建相应特效
-> 朝玩家头部发射,飞行速度较慢,便于玩家躲避
需求分析->
枪
{
枪Gun-> 开火,更换弹匣
单发枪 SingleGun -> 继承自Gun
连发枪 AutomaticGun -> 继承自Gun
}
子弹
{
子弹Bullet-> 计算攻击目标->移动-> 创建接触特效
(玩家检测环境,敌人)
(敌人检测环境?)
}
}
day 17
{
[RequireComponent(typeof(AudioSource))]
class Gun:MonoBehaviour
{
声音源?
private AudioSource audioSource;
//发射子弹时的声音片段
public AudioClip clip;//弹匣容量
//当前弹匣内子弹数
//剩余子弹总数
//为子类提供重新方法
protected virtual void Start()
{
}
public void Firing(Vector3 direction)
{
//玩家-> 枪口方向
//敌人-> 从枪口位置朝向玩家头部
//准备子弹
//判断弹匣是否包含子弹、是否在换枪...
//
//创建子弹、播放音频、播放特效、播放动画
//动画包括开枪,换弹
audioSource.PlayOneShot(clip);
}
public void UpdateAmmo()
{//更换弹匣函数
//
}
{//单发枪if(Input.getButtonDown(0))
{
this.firing(...) //输入枪口方向位置
}
public class AutomaticGun:Gun
{//连发枪//重构
protected override void Start()
{
base.Start();
....
}
if(Input.getButtonDown(0))
{
this.firing(...) //输入枪口方向位置
}
{
//计算目标点
//射线检测//移动
//到达目标点:销毁,创建相关特效
//创建相关特效
//根据目标点物体的标签
//hit.collider.tag;
private void GenerateContactEffect()
{
!!!
//通过代码读取资源
//资源必须放到Resources目录下
GameObject prefebGo =
Resources.Load
{
//根据敌人部位减血//
base.hit.collider.name
{ private void OnTriggerEnter()
{
//和玩家接触
//玩家减血
}
{
{
HTC VIVE BT下载
头戴式显示器
无线手持控制器
定位器
}
{
Menu button
Trackpad Left
Trackpad Up
Trackpad Right
Trackpad Down
System Button
Trigger (扳机,确认) (可以获取按下程度值)
Grip Button ()
}
定位器
{
靠激光和光敏传感器确定移动物体位置
}
{
Scripts->
Controler MangerTrack Object
GetPressDown
GetPressUp
GetToughDown
GetToughUp
控制器激光(贝塞尔曲线)
,指针,玩家传送,控制器抓取,UI
using VRTK;
回调事件->
VRTK_ControllerEvents events = GetComponentInParent
events.TriggerPressed += events_TriggerPressed;
//+= 后连按两次Tab-> 自动生成方法和值
void events_TriggerPressed(object sender,ControllerInteraactionEvent…)
{
//。。。要做的事情
}
Liner Point
BasicTeleport ->只能传送水平位置
HeightAdjustTeleport ->带高度传送
{
UGUI
unity Graphical User Interface
Rect Transform Layout Group
鼠标指针 拖拽类 点选类 输入类
开发效率暂低于NGUI?
画布之间-> Sort Order ->大的盖小的
Screen Space 和摄像机保持静止
{
Overload 覆盖
Camera ->摄像机模式,如果摄像机没有提供
泽仍然是Overload模式->
UI属性随着摄像机属性调整
Plane Distance ->摄像机和画布的距离
-> 渲染-> Depth Only
-> Culling musk -> 只看UI
投射方式-> Projection - > Orthographic
}
{
实现3DUI
}
{
Pos ->控件
轴心点相对于锚点的位置轴心点为物体中心,影响缩放,旋转
控件转-> 按照z轴转
设置轴心点->
Transform.Pivot.x(y) 取值(0,1)-> 物体内
超过1,0 物体外
中心点-> (0.5,0.5)
锚点 -> 4个小三角
锚点根据屏幕大小变化。
图片固定在右上角 ->
锚点放在右上角
锚点分开-> 根据图片缩放
锚点-> {大小自适应(背景),
位置自适应(血条,地图)}
Skretch -> 拉伸
如果画布是overload->
世界坐标= 屏幕坐标
this.transform.localPostion ->
当前轴心点相对于父UI的轴心点位置
获取RectTransform
RectTransform rtf =
this.getComponent
day01
{
集合
(修改挪用其他Shader)
(反光效果。。。)
(EasyTouch EasyAR ios发布)
集合
{
一种数据容器->数据结构
非泛型集合
泛型集合
System.Collections ->非泛型集合
System.Collections.Generic ->泛型集合
->性能不好、可能发生装箱
->类型不安全,发生类型转换异常
->有时需要做类型转换
(泛型)List
(泛型)Dictionary
1.删除指定元素 ->remove
2.移除指定索引的两个元素
->removeRange
3.指定索引插入元素
->Insert
Dictionary
new Dictionary
private Dictionary
new Dictionarydic02.Add("张三",new Dictionary
{
Push() 压栈Pop() 弹栈
Peek() 读栈顶但不移除
count
private Stack stac = new Stack();
练习: 主菜单->选项->游戏选项->难度调节->一般
队列 Queue(先进先出) Queue
{
Enqueque () 入队
Dequeue() 出队
Peek 读队首
{ PlayerPrefs
SetFloat()
SetInt()
SetString()
GetFloat()
GetInt()
GetString()
存->键值对
取->键
PlayerPrefs.SetInt("Score",number);
PlayerPrefs.HasKey("Score");
PlayerPrefs.GetInt("Score");
//!!要在场景中能够切换
//必须要将场景加入buildSetting中
//场景切换
//换关
SceneManger.LoadScene("name")
删除数据->DeleteAll
DeleteKey
案例应用->换装
GoblinClothRender.material.mainTexture = Cloth[i % Cloth.Length];
换武器
{
Render
Mesh
}
day02
{
NGUI
new EventDelegate(OnButtonClick));
{
private void Start()
{ for (i = 0; i < this.transform.childCount; i++)
{
CurrentButton = transform.GetChild(i).GetComponent
{
print(EventSystem.current.currentSelectedGameObject.name);
}
{
↓↑
工厂(资源加载)
}
day20:
{
协程 ->也是一种程序组件
每次协程被调用时,从协程返回(yield return)的位置接着执行
(迭代器?)
IEnumerator 接口 提供迭代
yield return XXX 指定下一次迭代入口
WaitForFixedUpdate 在FixedUpdate后执行
(物理控制)
null、0、WaitForSeconds 在每帧Update后执行
(分多帧完成一个任务,如平移)
WaitForEndOfFrame 每帧结束后执行
(每帧末尾执行,适合相机控制)
WWW 在请求结束后执行
(适合网络下载数据)
StartCoroutine 开启协程
{
yield return 1; ->停一帧
print(“1”);
yield return new WaitForSeconds(2);
->停2S
print(“2”);
yield return 1;
print(“3”);
}
自己定义了协程IEnumerator WaitTimeFunc(float TimeCount)
if(GUILayout.Button(“开始记时”))
{
StartCoroutine(WaitTimeFunc(3));
}
WWW www = new WWW(url);
yield return www;
//等待在一个网站上下载一个图片
render.material.mainTexture = www.texture;
StopAllCoroutines() 关闭所有协程
StartCoroutine(Fun(a))
{
private void OnGUI()
{ if (GUILayout.Button("Start"))
{
StartCoroutine(MovePath());
print("如果方块还未到终点,显示了我。说明协程是单独运行的。!");
//结果表明方块还没到时就显示了这句话!
}
}
private IEnumerator MovePath()
{
foreach (var point in Points)
{
//协程嵌套
//找到目标点后再找下一个点,等待MoveTotarget执行完毕再次进入下一个循环。
yield return (StartCoroutine(MoveToTarget(point)));
}
}
private IEnumerator MoveToTarget(Transform Target)
{
//注意这个循环执行完毕才会出去->即到了这个点
while (this.transform.position != Target.position)
{
this.transform.position = Vector3.MoveTowards(transform.position, Target.position, 0.5f);
yield return new WaitForFixedUpdate();
}
}
StopCoroutine(string a) ->
只能停止以StartCoroutine(string a)开启的协程
StopAllCoroutine()->
停止***本对象***中开启的所有协程
day21
{
文件与IO
System.IO中->
主要方法: GetDrives
{
Creat
Delete
Move
Copy
Exists读写文件
ReadAllText ->String
ReadAllLines -> string按行形成
ReadLines -> 一次向下读一行
ReadAllBytes -> 文件内容读入内存,以字节数组的方式保存
WriteAllText ->一次写入
WriteAllLine -> 每写一行换行
WriteAllBytes -> 写入字节
比File多了一个Length属性,可以了解一个文件所
占的字节
{
CreatDirectory 创建文件夹
{
GetFileName; 取得文件名,包括扩展名
{ FileStream
读取流(Read)
写入流(Write)
流可以支持查找(Seek,Position)
{
ReadLine
Read
ReadToEnd
}
StreamWriter:内容写入流
{
Write
WriteLine
}
{
Stream stream = new FileStream(path,fileMode.OpenOrCreate);
byte[] data = new byte(stream.Length);//输入参数 字节数组,读取位置,读取数量
stream.Read(data,0,(int)stream.Length);
//流使用完需要关闭,否则资源不会释放
//写
stream.Write(data,0,(int)stream.Length);
//刷新
stream.Flush
stream.Close();
//解码
return Encoding.UTF8.GetString(data);
Unity 寻路系统
{
路点寻路
单元格寻路
网格寻路实现寻路方法->
{
确定寻路者
烘焙寻路路面
程序实现寻路算法
}
障碍物->静态(static)
Windows->Navigation->右下角Bake
组件->Navigation->Nav Mesh Agent
private NavMeshAgent agent;
public Transform target;
private void Start()
{
agent = this.getComponent
private string path =
@“D:/aa/bb”;
{
if(!Directory.Exists(path))
{如果没有文件夹
Directory.CreatDirectory(path);
}
}
//ture则会删除所有子文件夹
DirectoryInfo info =
new DirectoryInfo(path);
if(!Info.Exists)
DirectoryInfo.Create();
->不加密文件夹,不会放大
->可以存放配置文件
}
Path.Combine(
Application.streamingAssetsPath
,“Config.txt”);
//去除前尾字符
a.Trim(); -> 修剪 去除空白行
a.Trim(’’); -> 去除左右两边的
{
string mainKey = null;
string subKey = null;
string Value = null;foreach (string line in lines)
{
string lineProcess = line.Trim();
if(!string.IsNullOrEmpty(lineProcess))
{
//该行不为空
if(lineProcess.StratsWith("["))
{//取主键
//mainKey = lineProcess.Substring(1,line.IndexOf("]")-1);
mainKey = lineProcess.Trim('[',']');
dic.Add(mainKey,new Dictionary
Off Mesh Link组件 -> 给点
搭桥点需要放在寻路网格上
{
radius ->碰撞半径,设置物体半径,寻路碰撞
height ->高度
Base Offset -> 偏移Speed -> 移动速度
Angular Speed -> 角速度 一般设置为120
Acceleration ->加速度
Stopping Distance -> 停止距离
Auto Braking -> 自动刹车
Obstacle Avoidance->躲避系数
Quality -> 等级
Priority -> 同级优先度
Area Mask -> 区域遮罩
Cost ->路径成本
Nav Mesh Obstacle 寻路碰撞,能动又能挡寻路物体
{
Shape 形状(只针对寻路)
{
Agent Radius -> 离障碍物的距离
Agent Height -> 寻路网格高度,基于路面
Max Slope -> 最大坡度Step Height -> 台阶高度
代码实现->
{
避免重复计算(位置不变不要重新计算)Unity寻路系统的动态碰撞有局限性
{
A* PathfindingSimplePath
{
射线
{ private void Update()
{
if (Input.GetMouseButtonDown(0))
{
//计算射线点,移动到设置此点位为目标点
//移动到目标点
CalcuteRay();
navAgent.SetDestination(Target);
StopAllCoroutines();
StartCoroutine(StartAnim());
}
}
private void CalcuteRay()
{
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawLine(ray.origin, hit.point,Color.red);
Physics.Raycast(ray, out hit,100f);
Target = hit.point;
}
private IEnumerator StartAnim()
{
while (Vector3.Distance(this.transform.position, Target) > 0.1f)
{
anim.CrossFade("Run");
yield return new WaitForEndOfFrame();
}
anim.CrossFade("Idle");
yield return 1;
}
mapCamera.ScreenPointToRay(Input.mousePosition);
=> 实现小地图点坐标点
camera.main.WorldToScreenPoint()
public class MovingHouseDemo : MonoBehaviour
{
private Transform furnitureTF; //要搬的家具
{
PickUpfun();
{
if (Input.GetMouseButtonDown(0))
{
if (UporDownFlag == false)
{
if (Physics.Raycast(CenterPointTF.position, CenterPointTF.forward,out hit,200F,layer)) //射线击中家具
{
furnitureTF = hit.collider.transform;
furnitureTF.GetComponent().isKinematic = true;
furnitureTF.parent = CenterPointTF; UporDownFlag = true;
}
return;
}
else
{
furnitureTF.parent = null;
furnitureTF.GetComponent
a.tranform.parent = b.tranform;
a.tranform.parent = null;
line.SetPosition(1,终点位置)
{
Physics.RaycastAll() -> 返回射线碰撞的所有物体的hits列表重叠球射线
-> OverlapSphere 重叠球
->返回值-> 碰撞器
private collider[] others;
others = Physics.OverlapSphere(this.tranform.position,2);
//半径为2
CapsuleCast 胶囊射线
}
!day23??->项目P277-279
{}
{
Mecanim 动画系统
包含Avatar的创建和对肌肉的调节
Animator Mecanim动画系统组件AnimatorController 动画控制器
Avatar Mecanim可识别骨骼
Retargeting 角色之间的动画互用
Legacy (使用Animation动画系统)
Generic 非人型(使用Animator)
Humaniod 人型(使用Animator)
动画参数 parameters +
{
Float
Int
Bool
Trigger
}anim = getComponent
day25
{
Layer ->实现多个动画同时执行
->边跑边攻击Weight 权重
Mask ->Avatar 遮罩 ->New 一个
播放到某种程度时-> 触发方法
->双击->Animator anim;
int idle = Animator.StringToHash("Base layer.Idle")
AnimatorStateInfo currentBaseStage;
currentBaseStage = anim.GetCurrentAnimatorStateInfo(0);
//0为默认层
if(currentBaseStage.nameHash == idle && !anim.IsInTransition(0))
//当前的哈希值和获得的相等,且不在过渡线上
{
...
}
{
碰撞,关节,布料…
铰链关节
弹性关节
固定关节
角色关节
可配置关节
}
{
Connected Bodyspring -> 弹性
Motor -> 马达
Limit -> 限制->
{
//可以实现逐级减少
break Force ->断开力
break Torque -> 断开扭矩
}
加力
other.rigidbody.AddForce(Vector3.forward * 500);
//斜上方
other.rigidbody.AddForce((Vector3.forward+Vector3.up) * 500);
MonoBehaviour类里面可以找到各种预制函数
{
//会自动添加Skinned Mesh Render
//Cloth
//点最大嵌入到mesh的程度
Blending Stiffness 弯曲硬度
Use Tethers 绳索属性
Use Gravity 重力
Damping 阻尼
Extern Acceleration 外部加速度
Random Acceleration 随机加速度
World Velocity Scale
World Acceleration Scale
Friction 摩擦力
Collision Mass 碰撞质量
Continuous Collision 增加消耗,减少直接穿透碰撞几率
Movie Texture 影片纹理
{
//已弃用,使用Video Player代替
using UnityEngine.Video;
Stop
Pause
}
{//城市勇士 小项目
//角色射线打到怪物,角色播放攻击动画,怪物播放
//受到攻击动画
AnimationEvent animEvent = new AnimationEvent();
AnimatorClipInfo[] clipInfo = anim.GetCurrentAnimatorClipInfo(0);
animEvent.time = 5;
animEvent.functionName = “AnimEventFunc”
clipInfo[0].clip.AddEvent(animEvent);
}
{
3dMax
{
a.调单位为厘米,自动保存,max点大小
Customize->Unit Setup-> 显示单位为米,系统单位设置为厘米
max和unity单位相差100倍
}
一直按住Shift 复制
Z 快速将物体移动到中心
F4 线框显示
Alt + Q 单独物体显示 (变回来点下面中间的框框)
穿过的线都要选中!!!
https://www.cnblogs.com/dawenhao/p/10406621.html
Pivot/Center:现实游戏对象的轴心参考点。Center为以所有选中物体所组成的轴心作为游戏对象的轴心参考点(常用于多物体的整体移动);Pviot为以最后一个选中的游戏对象的轴心为参考点。
分离-> Detach
{
避免贴图拉伸
不同位置有不同贴图
利于PS上贴图(三维模型点信息转为二维)搜索框输入UV ->找到UVW
->Edit
P288
给物体添加棋盘格->快捷键M(调到材质球)
->材质编辑器->编辑->精简模式
选一个材质球
->Diffuse漫反射 ->右边的框框->选择Checker(棋盘格)
->上面的小图片中选择->视口中显示阴暗处材质
->将材质指定给选定对象
设置tiling(瓷砖)-> 调高密度
//贴图 -> 展平贴图
Mapping -> Flatten mapping
UV缝合-> tool(工具)->Stitch Selected(缝合)
UV保存-> 转Poly
{
P311开始。 UV贴图
Ctrl+ T 缩小图片
Shift 等比例缩小
选择材质
Ctrl+D 取消选择
PS -> 快速选择工具 -> 从已有图里面拿贴图
不完全重合->在UV里面改模型,通过背景
{
Ctrl + T -> 图片放大/缩小
一直按住Shift 整体放大/缩小
Alt + 滚轮 放缩视图
调整大小时-> 右键旋转
Ctrl + Alt + Z 撤销长按Alt 复制
Ctrl + D 取消选择
-> 使用png格式(最常用)
{
原画(设计) ->
建模师(建模-分UV-上贴图)
-> 动画师(骨骼匹配 蒙皮)
->高模(精模) 低模(简模)
次时代技术(凹凸/法线贴图) -> 高模 建模 贴图->法线贴图-> 应用于低模
骨骼优化-> 关节数量
右键添加新层->新建剪辑->导入文件
->添加完成->点击行为树根->计算合成Compute Mixdown
Biped 框退出混合模式
{ P323
{
匹配骨骼-> Bip创建等身骨骼
模型透明化(Alt+X),模型冻结(右键选择)改变骨骼大小-> 运动中进入体形模式->
调整骨骼大小与模型适配(缩放,调型)
蒙皮-> 取消冻结-> 修改面板-> Skin 蒙皮
->骨骼。添加
取消透明显示
权重设置->Skin蒙皮->编辑,勾选点
下面一个钳子的符号
选择灰线(会看到皮肤上的蓝色点,为被影响的点)->设置相应权重。
显示-> 隐藏骨骼
导出-> 路径尽量不要有中文->放入材质
{
动画-> Animation->Clips
切割完成
Apply
}
{
1.单位比例
没有特殊要求下,单位为米,
3dmax中要设置系统单位为厘米,显示单位为米
https://blog.csdn.net/a1780531/article/details/90933558
unity中一个单位为一米,
3dmax中一个单位为一厘米,相差100倍坐标轴-> Max中坐标轴调整为y轴朝上
多余面数->删除场景中多余的面(看不见的面)->提高贴图利用率
避免闪面(原因 面与面之间距离太小。)->注意检查共面、漏面、反面(材质在背面去了,需要进行法线反转)
面与面之间的最小距离为当前最大尺度的2千分之1
{
法线反转->修改->法线(Normal)
}
P328
2.建模规范
树木模型-> 用十字交叉树或简模树
考虑和周围建筑的关系
重点建筑需种简模树,并在原地与之对应的种上十字片树(替换用)
总体原则-> 控制面数
十字交叉树
{
两张树图垂直交叉(透明贴图)
}
模型级别-> 模型级别/惊喜程度。
模型质心点
{
1.无特殊要求归于中心
2.所有地形上的建筑质心为底部中心
3.所有角色模型站立在原点
}
面数(移动平台)
{ ?感觉现在已经不止了
角色 主角模型控制在900-1300三角面,贴图控制在1张最好256*256,最多512*512
小怪 600-900三角面
最大256*256
武器面数 150 256*256/128*128
场景面数 10000 最多2张512*512
所有单个模型不能出现超过20000三角面的情况
否则导出可能出错?
}
!如果在MAX中对模型进行过缩放,需要加修改编辑器修正
选择缩放后的物体-工具(扳手形状)->
Reset XForm 重置变换->Reset Selected-
-> 转位Poly(保存更改)
3.材质贴图规范
3dsMax不是所有的材质都被Unity支持
支持{
Standard
Multi/Sub-Object(多维/子物体材质)
}
P329
贴图通道和类型
{
只支持Bitmap贴图类型
只支持Diffuse(漫反射)
和Self-Illumination(自发光)
贴图通道
}
贴图文件格式和尺寸
{
unity支持格式
{PSD,TIFF,JPG,TGA,PNG(性能较好),GIF,BMP}
不支持: JPEG,DDS(不完全)
PSD(PhotoShop格式)中层在导入Unity之后会自动合并
GIF导入后只显示第一帧
贴图文件尺寸 必须为2的N次方
最大贴图尺寸不超过2048*2048
UI贴图尺寸可以不遵循2N次方设计
}
4.命名规范
{
1.不能使用中文等 GBK格式命名,不能重名
2.美术资源后缀统一为小写字母
3.材质命名-> "mat"开头 mat_wenli_al
4.贴图命名-> "tex"开头 tex_wenli_al
}
5.模型导出
{
1.合并顶点(焊接/!目标合并)、清除场景、删除无用物件
2.无特殊需求不导出灯光、相机
3.按要求导出fbx,导出fbx后导入max看动画是否正确
4.一般导出默认选项,如无动画则不导出动画
}
{
(Effects)
private Render render;
render.material.shader = …;
GPU执行,针对3D对象进行操作的程序
CG: 与DirectX 9.0
以上以及OpenGL完全兼容,运行时编译成GPU汇编代码
GLSL:主要用于OpenGL。 移动平台(IOS,安卓,mac)
{
Windows: Direct3D,GPU汇编代码
mac:OpenGL GPU汇编代码
flash:flash GPU汇编代码
ios/android: CG转换为GLSL代码
}
一个像素包含如下缓冲
{
颜色缓存 color buffer/pixel buffer 即将显示的颜色
深度缓存 depyh buffer/z buffer 储存深度值
模版缓存 stencil buffer 限制渲染区域
累积缓存 Accumulation Buffer 与颜色缓存类似,为合成多幅图像设计->
在保持好的颜色分辨率下"多重曝光"。产生许多图像效果来提高图像真实性->
包括-> 反走样、运动模糊、软阴影、景深、卷积。对图像渲染多次,对场景位置(物体)进行微小的、渐增的改变并累积结果。}
图元装配(Primitive Assembly): 经过变换的顶点被装配成几何图元
光栅化(栅格化 Rasterization): 三角形图元(矢量)转换为像素碎片的过程。
光栅操作: 碎片处理后,在更新帧前最后执行的一系列操作: 裁剪,深度测试,Alpha(透明通道测试),Alpha混合。
碎片-> 更新像素潜在需要的一个状态,不等于像素。
P333
Shader按管线分类分为固定渲染管线和可编程渲染管线
->1. 固定渲染管线 : 标准几何&光照,功能固定
2.可编程渲染管线: 对顶点运算和像素运算分别编程处理。
计算机图形学深造->
https://www.zhihu.com/question/41468803
Unity中三种自定义Shader
{
1.surface shader 表面着色器
2.vertex and fragment shaders
顶点和片元着色器
3.fixed function shaders
固定功能管线着色器
}
ShaderLab基本结构
{
shader "name"
{
Properties{
//属性
//查看ShaderLab:Properties
_Cube("Cubmap",CUBE) = ""{}
//立方体贴图-> 反射效果
}
SubShader{
Tags{"RenderType" = "Opaque"}
//描述渲染类型-> 不透明物体
//Tags{"RenderType" = "Opaque" "queue" = "transparent"}
// ->透明的物体
//最优,最少要有一个SubShader
//查看ShaderReference
Pass{
//pass通道
//存储图像中的色彩等
}
}
SubShader{
//其次,上一个执行失败执行这个
}
FallBack "Diffuse"
//回退,全部失败,则执行默认
}
}
Properties 属性
Material 材质
Lighting 光照
Settexture 设置纹理
Pass Pass通道-> 存储图像色彩
shader 不区分大小写。
[_Color] //[]使用参数值
Color(1,0,0,1) //()使用固定值
光 / 高光 ->光照直射位置 / 环境光
!!
CGPROGRAM //CG代码块
#pragam surface surf Standard fullforwardshadows
//surface 表面着色器
//surf 调用的方法
//Standard 基于物理的光照模型(原来是漫反射)
//fullforwardshadows 阴影表现
//pragma target 3.0 //GPU硬件支持3.0 不再默认2.0
sampler2D _MainTex; //CG代码块中需要重新声明
struct Input{
float2 uv_MainTex; //记录UV纹理坐标
}
half -> 浮点型
fixed4 -> CG中的四阶向量
void surf(Input IN,inout SurfaceOutputStandard o)
{
fiXed c = tex2D(_MainTex,IN.uv_MainTex) * _Color;
O.Albedo = c.rgb;
o.Metallic = _Meaatllic; //金属光泽表现
O.Smoothness = _Glossiness; //高光光泽度
O.Alpha = c.a;
}
ENDCG
Cubemap
INTERNAL_DATA; ->
既要反射又要与法线配合时使用
例程
{
Shader "Custom/Diffuse Texture"
{
Properties
{
_MainTex("Texture",2D) = "white"{} //纹理
_BumpMap("BumpTex",2D) = "bump"{}
_Cube("Cubemap",CUBE) = ""{} //立方体贴图
}
SubShader
{
Tags{"RenderType" = "Opaque"} //描述渲染类型-> 不透明物体
//***********************************************
CGPROGRAM
#pragma surface surf Lambert //!!!
struct Input{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
INTERNAL_DATA //获取图片内部信息
};
sampler2D _MainTex; //CG里面再声明一遍
sampler2D _BumpMap;
samplerCUBE _Cube;
void surf(Input IN,inout SurfaceOutput o)
{
o.Albedo = tex2D(_MainTex,IN.uv_MainTex).rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap,IN.uv_BumpMap));
//与法线贴图配合金属反射
//WorldReflectionVector 函数计算逐个像素向量
o.Emission = texCUBE(_Cube,WorldReflectionVector(IN,o.Normal)).rgb;
//普通金属反射
//o.Emission = texCUBE(_Cube,IN.worldRefl).rgb;
}
ENDCG
//************************************************
}
FallBack "Diffuse"
}
}
{
先跳过-> 搞不到可用的NGUI包
{
->形成动画
-> 删除背景 - > 保存
或者-> Sorting Layer
2D刚体 ->
{ 质量 线性阻力/角度阻力
重力大小
锁定轴
}
private void OnCollisionEnter2D(Collision2D collision)
private void OnTriggerEnter2D(Collision2D other)
Used By Effector 使用碰撞效应
Area Effector2D 地区效应->特定地区反作用力不同
{
Use Collider Mask 遮罩
Use Global Angle 使用全角度
Force Angle 力左右角度
Force Magnitude 应用力的大小
Force Variation 力的大小变化
Drag 阻力 角阻力
}
{
Use Collider Mask 遮罩
Surface Level -> 浮力液体表面
Density 密度
Linear Drag 线性阻力
Angle Drag 角度阻力
Flow Angle 流动力在世界坐标的角度方向
}
{
Use Collider Mask 遮罩
Force Magnitude 应用力的大小
负值->吸附 正值->排斥Force Variation 力的大小随机变化
Distance Scale 应用于源和目标之间的距离
Drag 线性阻力
Angular Drag 角度阻力
Force Source 力源点吸引或排斥
Force Target 力应用目标
Force Mode 力计算的模式
Constant 恒定的
{
不要勾选IsTrigger实现单面碰撞->马里奥里面的单面弹簧
{
不要勾选IsTrigger
实现传送带效果
{
1.控制主角上下移动
2.每间隔2s,在两个随机诞生点随机产生2种怪物,
碰到城堡后(碰撞器)城堡掉血并且敌人播放爆炸动画3.子弹打到敌人,敌人播放爆炸动画
4.随着敌人不断诞生,敌人移动速度越来越快
5.防御技能:3个UI按钮-> 减慢敌人移动速度,城堡加血,销毁所有敌人
6.倒计时->一定时间后产生BOSS
精灵分割-> 锚点定在不动点
{
P378
1.配置Java开发环境
指定SDK地址->
->获取触摸方法
{
{
两者都要有碰撞器
动的物体有刚体
不勾选IsTrigger
Oncollision(Collision collision)
{
两者都有碰撞
动的物体有刚体
最少有一个勾选IsTrigger
OnTriggerEnter(Collision other)
常用功能 写常用Shader
挪用其他Shader
)
{
项目需求-> 开发一款简单的ARPG游戏项目
->ARPGDemo
核心系统:
{
1.角色系统:角色创建,属性管理
2.运动系统:转向,移动
3.技能系统:技能管理,技能释放
4.动画系统:动画播放,事件处理
5.AI系统 :AI操控,感知,寻路
6.成长系统:等级管理,经验管理
7.任务系统:任务,任务完成统计,任务管理
8.背包系统:装备,物品管理,升级
程序框架,人工智能
AI 人工智能
{
1.类-> 一组相似事物的统称
2.字段 方法
3.语法
class Student
{
public Grade g; private void test();
}
!!P404 设计原则
类与类的关系:
{
1.泛化: 类与类的继承关系。耦合度高
{
继承:泛化 -> 得到父类 向上 刀,枪-> 武器
特化 -> 得到子类 向下 武器-> 刀,枪
}
2.实现: 实现类与接口之间的关系
3.关联: 整体与部分的关系 -> 一个类作为当前类的成员出现
Head{eye,mouth,nouse}
4.依赖: 类与类的协作关系。另一个类作为当前类型的函数的参数或返回值
Class A{Test(Courese c)}
}
6.里式替换原则:
1.父出现的地方可以被子替换。
2.要保护替换前的原有工作,在替换后依然保持不变
override public void F1()
{
base.F1();
//。。。
console.writeLine("aaa");
//扩展
}
3.子类在重写父方法是(虚方法重写),尽量选择扩展重写。
7.接口隔离原则:
定义小而精的接口,少定义大而全的接口
交互-> 建立交互:找关系
移动->调用马达移动方法->动画系统播放移动动画
Framework
设计->
步骤1: 发现潜在数据类型、潜在字段、潜在方法
步骤2: 确定对象的数据类型
步骤3:
确定最终的设计结果
设计成果: 系统类图 ->文字、图片
5实现:根据类图 实现代码
1.根据类图,构建框架
2.根据开发经验 -> 实现类的细节:
确定字段准确类型,访问性
确定方法的返回类型参数,访问性
确定方法体
6.类的成员-> 字段、方法、【属性、索引器、事件】
确定对象数据类型-> 枚举/结构/类/接口/委托
确定方法类别 -> 普通方法/抽象方法/虚方法/重写方法/隐藏方法
ArpgDemo实现 P408
类图 -> 使用类图实现可视化框架搭建
day37
{
!构造函数不能继承
EasyTouch事件通知的使用方式->
{
1.设置UI
2.注册事件
3.实现方法
}
public void JoystickMove(MovingJoystick move)
{
…
}
public void JoystickMoveEnd(MovingJoystick move){…}
{
EasyJoystick.On_JoystickkMove += JoystickMove;
//JoystickMove 为要注册的函数名
EasyJoystick.On_JoystickkMoveEnd += JoystickMoveEnd;
}
private void OnDisable()
{…}
{
LookAtTarget(new Vector3(x,0,z));
move.joystickAxis.x,move.joystickAxis.y
{
private CharacterController charcontroller;
{
anim = this.GetComponent();
charcontroller = GetComponent();
}
{
if (x != 0 || z!= 0)
{
//1.转向
LookAt(new Vector3(x, 0, z));
//2.移动
//-1 -> 模拟重力,保证主角贴合地面不会飘起来 ?
Vector3 motion = new Vector3(transform.forward.x,-1, transform.forward.z);
//transform.position = Vector3.Lerp(transform.position, motion, moveSpeedTime.deltaTime);
//速度控制
charcontroller.Move(motionTime.deltaTime*moveSpeed);
//3.播放动画
anim.PlayAnimation(“run”);
}
else
{
anim.PlayAnimation(“idle”);
}
} ///
class playercontroller
{
public void JoyStickMove(MovingJoystick move)
{
playermotor.Move(move.joystickAxis.x,move.joystickAxis.y);
}///
{
实现复用-> 不用每次从头开始
所有类继承Object方法,不需要写
{
复用代码
概念复用
层次化管理类
}
缺点->
{
耦合度高
不宜继承太深
尽量选择抽象类继承
}
{
父类中所定义除私有成员都继承给子类
构造方法不会继承
}
2.创建子类对象时,自动调用父类构造方法
且父类构造函数先执行,子类后执行
3.调用构造方法-> new 类时 -> 创建对象时
若父类有构造方法,则调用此方法,父类不再生成无参方法
父类构造方法有参数且需要调用:
子类中通过base实现调用有参构造函数
public Dog():base(123)
{}
传变量
public Dog(int id):base(id)
{}
5.构造函数相互调用
public Dog(string ev):this()
本类中调用用this
子类调用父类用base
至少满足单向is->
A is B or B is A
private 私有 只有当前类可用
protected 保护 当前类和后代可用
internal 内部 当前项目可用
public 共有 不受限制
internal(默认) public
private(默认) protected internal public
internal
private
{
base.Fun()
Fun()
}
case 2:
…! //switch实现 1||2执行同一个方法
break;
父调子 -> 难
{
多态-> 多种形态,对象的多种形态,行为的多种形态
Animal obj3 = new Dog();
方法重写,隐藏,重载
1.子的方法与父方法重名且重参数->
隐藏->子方法覆盖父方法
2.子方法与父方法重名但参数不同->
重载-> 两个方法
3.
}
{
1.基本方式: 隐藏,重写
2.其它方式:重载,接口
}
{
父类方法在子类不适合,子类定义同名同参数方法
但父方法不能重写(不是override,virtual,abstract)建议写法 有意隐藏->
new public void Fun()
Why: 为了选择性复用(父类方法在子类不适合)
What: 多态实现的一种方式
How: 使用步骤
{
1.父类定义普通方法
2.子类中定义隐藏方法(名和参数都相同) new public void Fun(){}
}
隐藏的缺点->
只能 子 a = new 子();
不能 父 a = new 子();-> 这样仍然用的父的
P427
}
{
在基类中定义虚方法->
virtual public void FUN(){}
在子类中定义重写方法->
override public void FUN(){}虚方法: 用Virtual关键修饰的已经实现的方法
含义:表示一个可以在子类中重写的方法
三种重写
{
1.虚方法重写
2.重写方法重写
3.抽象方法重写
}
三种方法可重写:
abstract->方法在子类中必须重写,
除非子类也是抽象类
virtual->方法在子类中可以重写
override-> 已经重写过的方法,在子类中可以继续重写,
除非被标识为sealed
方法重写时必须在方法前加入override
P428
1.虚方法重写:
在父类中定义虚方法,子类中定义重写方法
方法名相同,参数相同,返回类型相同
virtual public void FUN(){}
子类-> override public void FUN(){}
sealed(密封)
1.用在类的定义上->
提示当前类不能做父类,也就是任何类都不可继承当前类
sealed class A()
封闭类->可以实例化。
2.用在重写的成员,提示当前类的子类,不能重写该成员
sealed用在方法前,必须和override一起
sealed override public void Fun()
封闭方法
共性:多态实现方式,实现选择性复用
不同:
1.
2. 实现原理不同
隐藏: 静态绑定,性能略高,灵活性差
重写: 动态绑定,性能略低,灵活性好
绑定-> 系统确定一个类型能调用那些方法的过程
静态:运行前确定
动态: 运行期间(过程中)确定
重写 -> 改变方法地址-> 父 obj = new 子()
子 obj = new 子()
都调用子的方法
}
扩展重写->
class Dog:Animal
{
override public void Run(){
base.Run(); 在父的方法的基础上扩展
...
}
}
{
抽象类 -> 可继承不可实例化
{
例子-> Gun抽象类
Ak47 -> 继承Gun-> 一般类1.语法用abstract修饰抽象类
abstract class A{}
2.特点: 抽象类不能创建对象(不能实例化) 可以被继承
抽象类可能包含抽象成员(方法,属性)
(可为空,可有可无抽象成员)
抽象 具体 是相对的
抽象类运用场景->
1.表示一个抽象概念Gun->Ak47
2.不希望类创建对象
3.有行为,但是不需要实现的时候
-> 受到伤害,死亡(玩家和怪物是具体实现不同)
-> 父类实现管理,子类实现具体功能->
Virtual -> override 实现
abstract public void Run();
-> 定义抽象方法
分析时找角色找共性时
4.有多个概念,需要一致的管理时
5.一些行为,在做法上有多种可能且不希望客户了解。
{
1.希望做基类
2.分析时找角色共性
}
{
1.只有声明定义,没有实现的方法。
abstract public void Run();2. 实现类必须实现所有抽象方法
抽象方法必须放在抽象类或接口中
3. 抽象类中的抽象方法必须加abstract
->且不能私有
实现类实现抽象方法必须加override
->且不能私有
class Animal
{
string name;
abstract public void Run();
}
class Dog:Animal
{
override public void Run(){
...;
}
}
1.子类也是抽象,子类可以不实现抽象方法
2.父抽象子类实现不能为虚方法。
->子类override方法可以再次被重写
1. 未实现:已实现
2. 必须重写:可以重写
{
使用关键字interface创建的数据类型
接口名建议用"I"开头,其后单词首字母ISwitch,IFly…1.接口是一组行为的抽象
2.接口是规范,定义一组对外的行为规范。
要求他的实现类必须遵循。
3.接口中只有行为,不能有字段
->可以包含:行为【方法、属性、索引器、事件】
接口不能有访问修饰符
interface ISwitch
{
void On();
void Off();
}
class Door : ISwitch
{
public void On(){}
public void Off(){}
}
接口中的抽象成员,不能加abstract,不能加访问修饰符
实现接口的方法不能加override。
默认抽象、默认公共。
接口的作用—>
1.扩展一个已有类的行为
2.规范不同类型的行为,达到不同类型在啊行为上是一致的
提取不同类别的共性的行为,这个行为实现的最大的复用性
鸟/昆虫 有的会飞有的不会飞
->建立飞的接口
鸟 共性: 腿 羽毛 【行为】吃 走
昆虫
老鹰:鸟,飞的接口
蝴蝶:昆虫,飞的接口
飞机: 飞的接口
实现类实现可以实现多个接口,用:
接口的使用:
继承:
1.类继承类-> C#只能单继承
2.类继承接口【类实现接口】-> 可以多继承
3.接口继承接口
->接口继承接口,不可以实现接口功能 P438
interface IMy:IMotherEye
{
void Eye(); //✔
✖public void Eye(){}
}
4.结构可以实现接口,但不能继承类
struct MyStruct:IMotherEye{}
接口和接口之间可继承,且可以多继承
类与类是单继承,类与接口是多实现,接口与接口是多继承
结构可以实现接口,但不能继承【类】
类继承接口-> 继承了就必须实现接口
{
class Son2:IMotherEye
{
1.隐式实现-》常用
public void Eye(){}
2.显式实现 -> 不加修饰符,很少用
1.解决接口中的成员对实现类不适应的问题
2.解决多接口实现时的二义性
Void IMotherEye.Eye(){}
}
}
显示实现 接口调用—>
Son:Father,IMotherEye
{
void IMotherEye.Eye(){}
}
static void Main(string[] args)
{
IMotherEye son = new Son();
son.Eye();
}
namespace Homework10-12
{
class Client
{
static void Main(string[] args){ Shape[] shape = new Shape[3];
shape[0] = new Circle(3f);
shape[1] = new Triangle(1f, 2f);
shape[2] = new Rectangle(1f, 2f);
//!!!!!!!!!!!
foreach (IAreaCalculate a in shape)
{
Console.WriteLine(a.AreaCalculate());
}
//!!!!!!!!
}
{
//virtual public float AreaCalculate() { return 0; }
}
{
float AreaCalculate();
}
class Circle : Shape,IAreaCalculate
{
float r;public Circle(float r)
{
this.r = r;
}
public float AreaCalculate()
{
return (float)Math.PI * r * r;
}
{
float height,width;public Rectangle(float height, float width)
{
this.height = height;
this.width = width;
}
public float AreaCalculate()
{
return (float)height * width;
}
{
float width,height;public Triangle(float height, float width)
{
this.height = height;
this.width = width;
}
public float AreaCalculate()
{
return (float)height * width*0.5f;
}
结构 struct 不可以(只能接口) 可以 值类型
封闭类 sealed class 不可以 不可以 引用类型
一般类 class 可以 可以
抽象类 abstract class 可以 不可以 抽象类中可以有实现【字段,实现方法,抽象方法】,只能被单继承
接口 interface 可以 不可以 只有方法没有成员 接口中只有抽象成员【方法,属性】不能有任何实现,接口可以被多继承 接口是特殊的抽象类
一般方法 无 / 可以 可以
构造方法 无数据类型 new 类名(); 可以 static 类名 可以
析构方法 ~类名(){} 不用写
隐藏方法 new 子类.方法() 可以 不可以,结构不能继承
虚方法 virtual 类.方法() 不可以 不可以
重写方法 override 父 A = New 子();子.方法
不可以,静态成员不支持virtual,override,abstract 不可以
抽象方法 abstract(抽象类中)不能调用 不可以 不可以
无 (接口中)
重载方法 无 与原方法名相同 可以 可以
静态方法 static 类名.方法()
{
IComparable 比较接口
常用 使类型支持比较大小的功能
IComparer 比较器
常用于排序比较
IEnmuerable 枚举器
常用 使类型支持简单迭代foreach
IEnumerator 枚举器
支持MoveNext,可以自己控制迭代节奏
}
Student ls = new Student{Name=“ls”,Age = 22,Tall = 160};
{
public int Age{get;set;}
public int Name{get;set;}
public int Tall{get;set;}
{
return this.Age.CompareTo((obj as Student).Age);
}
}
class TallCompare:IComparer
{
public int Compare(obj x,obj y)
{
return (x as Student).Tall.CompareTo((y as Student).Tall);
}
}
写法1 zs.Age.CompareTo(ls.Age)
//如果要比较身高
-> TallCompare tallcom = new TallCompare();
int re2 = tallcom.Compare(zs,ls);
类-> 鸟
接口-> 飞
接口-> 游泳
{
委托 方法的代理-> 委托的是方法,当调用委托时就调用了这个方法
委托->一类行为的抽象。
是方法的引用,
是一种数据类型。
事件 -> 委托的一种使用方式
一种【声明了返回类型和参数组成】的一种数据类型 ->与class,enum,struct,interface同级
委托代表/表示/代理/的是方法->代表和委托签名一致的任何方法签名 含义不同 1.签名 = 返回类型和参数 【】
2.签名 = 方法名和参数【隐藏】
3.签名 = 方法名和参数组成,返回类型【重写】
//步骤1: 声明委托,定义委托
delegate void MyDelegate();
#习惯: 委托命名: xxxxHandler
#example SelectHandler
delegate void Handler();
Handler handler = new Handler(Fun);
handler();
1.单播委托 一个委托关联一个方法
2.多播委托 一个委托关联多个方法MyDelegateHandler handler = new MyDelegateHandler(SumTwoNumber); //注册委托事件
handler = handler + new MyDelegateHandler(Divided);
-> 注册多个委托
int re = handler(1,3);
->多播委托只返回最后一个方法的返回值
1.标准写法 1
Handler handler = new Handler(Fun);
2.标准写法 2
Handler handler = Fun;
3. 匿名方法 适合于简单少量代码可完成且在其它地方不需要复用
Handler handler = delegate(int a) 等号右边表示委托对象 = 匿名方法
{
return 888;
}
4.!Lambda 表达式
Handler handler = (p) => Console.writeLine(p + 123);
等号右边表示委托对象=Lambad 表达式=匿名方法
写法一 :(参数) => {语句体} //=>goto
写法二:一个参数=> 一行代码;
//!return不能写
static void Main(string[] args)
{
List personList = new List(4);
for (int i = 0; i < 4; i++)
{
personList.Add(new Person(i, 15+i));
} //1.
PersonHandler personhandler1 = new PersonHandler(ChooseId);
//2.
PersonHandler personhandler2 = ChooseId;
//3.
PersonHandler personhandler3 = delegate (List
{
public int Id;
public int age;public Person(int Id, int age)
{
this.Id = Id;
this.age = age;
}
1.方法作为参数传递,将方法执行代码注入带另一个方法中
方法1 (int string 数据数值)
方法2 (方法1语句块)Handler handerObj = aObj.F1;
aObj.F2(handerObj);
等价于->
aObj.F2(aObj.F1);
//方法F2注入lambda方法
aObj.F2(()=>{
Console.writeLine("1111");
});
public void F2(Handler handerObj)
{
handerObj();
Console.writeLine("2222");
Console.writeLine("3333");
}
a.暂时定不下来的功能,先通过委托确定
b.有些函数需要客户端确认
public void Fun(handlerOBJ buycai)
{
buycai();//买菜,但不知道能买到什么
做菜();
}
回调含义 1 方法 系统调用的 比如->调定点闹钟
2 定义时定不下来,留给客户端确定
a.同步调用: 排队调用,前一个方法执行时后一个等待
b.异步调用: 前一个方法如果是异步调用->
后一个方法不必等待它的结束就可启动。
实现方法-> 创建一个新线程。
步骤:
1.为异步调用定义一个相应委托
2.创建委托的引用指向需要异步调用的方法
3.使用委托类型的BeginInvoke方法开始异步调用
a.BeginInvok中的参数IAsyncCallback表示异步调用的方法结束时执行的回调方法
往往用回调方法来处理异步的结果
b.BeginInvok中的参数object表示回调方法中需要用到的数据,该数据被封装在IAsyncResult的AsyncState属性中 4.若方法有返回值,则需要调用EndInvoke取得返回值。
1> delegate void Handler();
class B
{
//声明外部
AsycCallback callbackObj = null;
2>实例化
A obj = new A();
Handler handlerObj = new Handler(obj.Fun);
3>开始异步调用
//BeginInvoke参数->
//AsycCallback参数->指定一个方法取结果
//object 用来传参数
handlerObj.BeginInvoke(callbackObj,null)
callbackObj = new AsycCallback(FunGetResult);
static void FunGetResult(IAsyncResult ar)
{
//4> 结束异步调用【如果有返回值,可以取返回值】
callbackObj.EndInvoke(ar);
}
}
例子 -> 但是我的平台报错说不支持这种操作
{
namespace Homework6
//通过委托实现异步调用
{ public delegate void asynchronousHandler(); //声明委托
static asynchronousHandler asynchronoushandler = null;
static void Main(string[] args)
{
A obja = new A();
asynchronoushandler = new asynchronousHandler(obja.Fun1); //注册委托
AsyncCallback callbackObj = new AsyncCallback(FunGetResult);
asynchronoushandler.BeginInvoke(callbackObj, null); //开启异步调用,结束时调用FunGetResult函数
obja.Fun2();
}
static void FunGetResult(IAsyncResult ar)
{
asynchronoushandler.EndInvoke(ar);
}
{
public void Fun1()
{
//执行事件比较长的程序
Thread.Sleep(3000);
Console.WriteLine(“1111111111”);}
public void Fun2()
{
//执行时间短的程序
Console.WriteLine("2222222");
}
}
https://blog.csdn.net/tangpeicheng/article/details/1427448
作为方法的参数传入
{
委托是事件实现的基础;事件是委托的一种使用方式。
对象1 的方法 调用对象2的方法
定义: 偶发,有影响,事件发生后其他对象做出响应
一种天然的通知模式
一个天然观察者模式
一种设计模式
实现两个或多个行为的联动调用。
事件的使用:【事件设计模式】【事件模式】
定义端-> 1,3 调用端2,4
1.定义事件
public event 委托类型 事件名;
(event可以省略)
2.注册事件
事件源对象.事件 += 响应方法
传统写法
事件源对象.事件 += new 委托类型(响应方法)
+= 在这里是注册符号和算数加法不同。
-= 取消注册
3.触发事件
【4】.调用触发事件的方法
程序设计:猫叫-> 老鼠跑,主人醒
1. 要触发
2. 考虑可扩展性
{
//实现事件 猫和老鼠
{
static void Main(string[] args)
{
Cat cat = new Cat();
Mouse mouse = new Mouse();
Houser person = new Houser(); cat.CatBark += mouse.Escape; // 注册老鼠跑的事件
cat.CatBark += person.WakeUp; // 注册主人醒的事件
//触发
Random random = new Random(); //注册种子
int j = 0;
for (int i = 0; i < 100; i++)
{
j = random.Next(i - 5, i + 5);
if (j == i)
{
cat.Notify(); //老鼠发现猫
}
}
}
{
public event CatHandler CatBark; //声明猫叫的事件public void Notify()
{
if (CatBark != null) //如果事件中注册了方法-> 执行
{
Console.WriteLine("miao~~~~");
CatBark();
}
}
{
public void Escape()
{
Console.WriteLine(“jiji~~~~”);
Console.WriteLine(“老鼠逃跑”);
}
{
public void WakeUp()
{
Console.WriteLine(“主人醒了”);
}
委托与事件的使用场景委托:
1.事件设计模式
2.封装静态方法
3.当调用方不需要方法使用该方法的对象
接口:
1.当存在一组可能被调用的相关方法
2.类中只需要方法的单个实现
3.当使用接口的类想要将该接口强制转换为其他接口或类类型时。
接口 obj = (接口) Dogobj;
4.当正在实现的方法链接到类的类型或标识时:
-> 比较方法
{
泛型类,方法,接口,委托,集合
List T: int string Student
方法1.
Class My 泛型类
{
ArrayList arrList = new ArrayList(); public void Add(T i) 泛型方法
{
arrList.Add(i)
}
}
-> 实例化
My
static class Tool
{
///
/// 使用冒泡算法对Array进行排序,Mode = 0 升序排序,Mode = 1 降序排序
///
/// Comparable
///
/// 0,1
public static void Sort(T[] Array,int Mode=0) where T : IComparable
{
int i, j;
if (Mode == 0)
{
for (i = 0; i < Array.Length; i++)
{
for (j = i; j < Array.Length - 1; j++)
{
CompareAndSwap(ref Array[j + 1], ref Array[j]);
} }
}
else if (Mode == 1)
{
for (i = 0; i < Array.Length; i++)
{
for (j = i; j < Array.Length - 1; j++)
{
CompareAndSwap
class Student:IComparable
{
{
return this.Age.CompareTo(other.Age);
day42
{
集合-数据类型
扩展方法:
List list = new List();
add,remove,clear
属性:Count,Keys,Values
Common:工具类【通用性很强,很多项目可以使用】
1.类数据类型
2.通用性强
3.一般不写命名空间,一般定义 Public static的类
4.类中方法一般为public static
a. 数组工具类: ArrayHelper
i. 排序:升序、降序
ii. 查找: 查找单个,查找全部
iii.选择:选取数组中对象的某些成员组成一个独立数组
{
public delegate int CompareHandler(T a ,T b)where T:IComparable; //声明委托,输入2个类
}
i. 容器 (用于存放缓存对象)
ii. 添加
iii.删除
iv. 取得概念:
1.对象池 -> 池,缓冲,缓冲技术
2.能放多个元素的容器
3.一种提高程序性能的技术,使用空间换取时间。
占用内存,提高速度
4.如何提高程序性能?
1.一般情况 需要对象-> 创建->不需要,清除
频繁创建->浪费性能
2.使用对象池
创建->使用->返回缓存到对象池
无需再次创建,节省再次创建时间。
5.如何使用对象池
频繁使用-> 子弹,小怪
对象池=池
游戏对象池 针对游戏对象使用池技术
实现-> 字典
实现: 类图实现【字典数据类型+unity游戏物体】
1.创建池
Dictionary
//外层-> 子弹 -> 12345 枪-> 6789
private Dictionary
/// 创建对象,池中有则直接返回,池中没有,则放入池中
///
/// 键
/// 预制件对象
/// 位置
/// 四元数
///
public GameObject CreateGameObject(string key,GameObject perfab,Vector3 position,Quaternion quaternion)
{
GameObject WaitLoadGo = FindUsable(key);
if (WaitLoadGo != null)
{
//存在可用的对象
WaitLoadGo.transform.position = position;
WaitLoadGo.transform.rotation = quaternion;
WaitLoadGo.SetActive(true); //设置启用,启用标志开启后该对象在池中不可用
}
else
{
//池中没有对象,创建对象并放入池中
WaitLoadGo = GameObject.Instantiate(perfab, position, quaternion);
WaitLoadGo.SetActive(true);
AddGo(key, WaitLoadGo);
}//将对象父物体设为池
WaitLoadGo.transform.parent = this.transform;
return WaitLoadGo;
/// 找到可用对象
///
/// 对应键
/// 空对象或者找到可用对象
private GameObject FindUsable(string key)
{
if (cache.ContainsKey(key))
{ //注意这里的Find是列表List的Find
return cache[key].Find((a) => (a.activeSelf == false)); //active标志位为false表示该对象还未被激活
}
return null;
/// //将新创建的对象加入池中
///
///
///
private void AddGo(string key, GameObject WaitLoadGo)
{ if (cache.ContainsKey(key))
{
cache[key].Add(WaitLoadGo); //注意这里的Add是列表List
/// 删除给入键的物体缓存
///
///
public void Clear(string key)
{
if (cache.ContainsKey(key))
{
foreach (var obj in cache[key])
{
GameObject.Destroy(obj); //清除对象
}
//清除键
cache.Remove(key);
}
/// 删除字典中所有键
///
public void ClearAll()
{
foreach (var key in cache.Keys)
{
Clear(key);
}
}
/// 回收对象
///
///
public void RetrieveObj(GameObject Go)
{
Go.SetActive(false);
}
/// //通过携程实现延时回收
///
///
///
public void RetrieveObj(GameObject Go, float delay)
{
StartCoroutine(RetrieveDelayCoroutine(Go, delay));
}
{
yield return new WaitForSeconds(delay);
RetrieveObj(Go);
单例设计模式:
定义: 本质上是一个类,特殊类特殊1: 构造方法私有
2: 包含一个静态私有字段,返回自己的类型
3. 包含一个静态公共方法 返回自己的类型
用途:
保证自己在应用程序域中 最多只有一个实例【对象】存在
得到唯一对象的唯一通道
class President
{
private President()
{}
public string Name{get;set;}
static private President obj = new President();
static public President GetObj()
{ //饿汉模式
return obj;
}
}
-》
static public President GetObj()
{ //懒汉模式
if(obj == new) obj = new President();
return obj;
}
1.单例对象池
private GameObjectPool() { }static private GameObjectPool pool;
static public GameObjectPool GetGameObjectPool
{
get
{
if (pool == null) pool = new GameObjectPool();
return pool;
}
}
2.借助(继承)EasyTouch的MonoSingleton
{
public delegate bool FindHandler(Transform tf);
{
if (tf.name == name) return tf; //递归终止条件int childCount = tf.childCount;
if (childCount > 0)
{
for(int i =0; i< childCount;i++)
{
Transform re = FindChild(name,tf.GetChild(i));
if (re != null) return re;
}
}
return null;
{
if (findMethod(tf)) return tf; //递归终止条件int childCount = tf.childCount;
if (childCount > 0)
{
for (int i = 0; i < childCount; i++)
{
Transform re = FindChild(tf.GetChild(i),findMethod);
if (re != null) return re;
}
}
return null;
{
if (target != Vector3.zero)
{
Quaternion dir = Quaternion.LookRotation(target);
transform.rotation = Quaternion.Lerp(transform.rotation, dir, rotateSpeed * Time.deltaTime);
}
{
按下技能按钮-> 调用技能系统释放技能->
调用动画系统播放动画->处理动画事件
-> 调用小怪受伤->可能调用死亡
->主角收集经验->升级
引入->
技能数据 SkillData
技能管理组件 SkillManager
施法器组件 攻击方 挂技能管理组件,选择要释放的技能,(释放器组件)动态创建技能预制件对象。
被击方 暂不实现
->选择要释放的技能
所有技能共性-> 存在攻击距离,范围,伤害敌人的类型,单攻或群攻,对自身影响,对敌人影响
{
1.技能数值可以调节
2.不同技能释放特性,动画,受击特效不同,可调节
3.玩家可选择随身技能
}技能数据类 SkillData
类成员:
技能ID (SkillID)
技能名称 (name)
技能描述 (description)
冷却时间 (coolTime)
冷却剩余 (coolRemain)
魔法消耗 (costSP)
攻击距离 (attackDistace)
攻击角度 (attackAngle)
攻击目标tags[] (attackTargetTags)
攻击目标对象数组 (attackTargets)
连击的下一个技能编号 (nextBatterld)
伤害比率 (damage)
持续时间 (durationTime)
伤害间隔 (damageInterval)
技能所属 (Onwer)
技能预制件名称 (perfabName)
预制件对象 (skillPerfab)
动画名称 (animationName)
受击特效名称 (hitFxName)
等级(level)
是否激活(activated)
技能类型(SkillAttackType attackType) :单攻Single,群攻Group
伤害模式(DamageMode damageMode): 圆形Circle,扇形Sector,矩形Rectangle
SkillData -> 不直接挂在物体上,间接挂在物体上
P497
[Serializable] -> 可序列化
->不直接给物体,在其它组件中使用,要在unity窗口访问其字段。
字段->
{
简单类型 在属性窗口赋值
复杂类型 不适合在属性窗口赋值
[HideInInspector]->不在属性窗口出现
}
技能管理类 Skill
{
数据: 管理多个技能数据对象
容器 List
///
private GameObject[] attackGameObjects;
protected IAttackSelector attackSelector;
protected List
释放器工厂类-> 初始化属性
{
public SkillData Skill {
get { return my_skillData; }
set {
if (value == null) return;
my_skillData = value;
attackSelector = SkillDeployConfigFactory.CreateAttackSelector(value);
selfImpacts = SkillDeployConfigFactory.CreateSelfImpactList(value);
TargetImpacts = SkillDeployConfigFactory.CreateTragetImpactsList(value);
}
}
}}
{
实现技能攻击动画以及外观类 :
事件: 注册: F1 【F2自动调用】
例:按下按钮-> 播放动画->触发动画事件-> 其它行为
!添加技能系统外观类: 技能系统中介。 引入目的-> 方便其它系统使用,保护当前系统的安全性和稳定性。
Skill -> {
AttackSelect->{IAttackSelector.cs,CircleAttackSelector.cs,SectorAttackSelector.csv,LineAttackSelector.cs}
ConfigFactory->{SkillDeployConfigFactory.cs //选择攻击接口,选择自身影响数组,选择目标影响数组}
SelfImpact->{ISelfImpact.cs,CostSpImpact.cs…}
TargetImpact->{ITargetImpact.cs,DamageTargetImpact.cs…}
LongRangeSkillDeployer.cs
MeleeSkillDeployer.cs 近战施法器
SkillData.cs //技能基本字段
SkillDeploy.cs //施法器
SkillManager.cs //技能管理类
}
Character->{
SkillSystem.cs //技能系统
}
{
优化:
资源管理的优化 Assests : 模型,组件,脚本…
-> Resources: 预制件资源(动态加载资源)
引入专门的文件: 生成地图文件->资源配置文件 [.txt = .ini]
特点: 键=值
技能名称= 技能的实际路径
BaseMeleeAttackSkill = Skill\BaseMeleeAttackSkill
Hit_Ice_Chest = Skill\HitFx\Hit_Ice_Chest
自动生成 -> 制作自动生成资源配置文件->使用IO技术。
GenerateResConfig.cs !-> 文件放在Editor文件夹,这些文件只能在编辑器下使用,不会被打包进游戏。
-》
//所有\ 变换为/ ,得到路径
string path = Path.Combine(Application.dataPath,“Resources”).Replace(@"","/");
string[] resFiles = Directory.GetFiles(path,"*.prefab",SearchOption.AllDirection);
{
1.读取资源配置文件
2.得到所需资源路径
3.
-> MenuItem 为unity增加菜单项。
程序集: .NET Framework 构造块 协同工作而生成的类型和资源的集合。
类库 -> dll 没有入口
关系 dll+入口 = exe
添加类库项目-> project2Library
{
代码,资源,信息(元数据)
}
{
1.了解类型信息。
2.动态创建类型实例。
3.动态访问实例的成员。
}
{using System.Reflection
Type objtype = Type.GetType(“namspace.A”); //namspace命名空间中的A
MemberInfo[] MArr = objtype.GetMembers(); //获取类中所有的成员
MemberInfo[] MArr2 = objtype.GetMethods(); //获取类中所有的方法
var MArr3 = objtype.GetEvents(); //获取类中所有的事件
适合于类型名未知,类型未知,存在已有对象。
Type objtype = (new A()).GetType();
…
Type objtype = typeof(A);
适合于类型在另一个程序集中 “D://…//…//prj2.dll”
static void Main(string[] args)
{
Type type = Type.GetType(“System.String”);
var resule = type.GetMembers();
}
Activator.CreateInstance(string 程序集名称,string 类型全名).Unwarp();
使用反射技术创建对象
Type typeobj = Type.GetType(“namspace.A”);
object obj = Activator.CreateInstance(typeobj);
Student stuObj = (Student)obj
MethodInfo method = typeobj.GetMethods(“Fun”);
method.Invoke(stuObj,null); //调用对象构造方法和输入参数没有写null
//有参数的方法: method.Invoke(stuObj,new Object[]{…});
FieldInfo field=typeobj.GetField(“Fun”);
field.SetValue(stuObj,…);
string allName = “ARPGDemo.Skill.”+skill.damageMode+“AttackSelector”;
Type typeObj = Type.GetType(allName); -》 得到类类型为typeObj
MethodInfo method = typeObj.GetMethod("");
object obj = Activator.CreateInstance(typeObj);
{ IAttackSelector attackSelector = null;
//获取类型名字全称,包括命名空间
string typeAllName = "ARPGDemo.Skill." + skillData.DamageMode.ToString() + "AttackSelector";
Type typeobj = Type.GetType(typeAllName);
//动态反射,使用反射技术创建对象
object obj = Activator.CreateInstance(typeobj,null); //构造函数不需要参数,传入null
attackSelector = (IAttackSelector)obj;
return attackSelector;
day46 网络编程 P533
{
tcp,udp 协议不同->
{
1.基于连接与无连接
2.对系统资源的要求TCP高,UDP少
3.UDP结构简单
4.TCP数据正确性高,tcp保证数据顺序,UDP可能丢包
流模式,数据报文模式?}
面向连接的协议,在收发数据前和目标建立可靠的连接
非连接协议,源端和终端不建立连接,想传送时简单去抓取来自应用程序的数据并。
http HyperText Transfer Protocol 超文本传输协议 网站 asp.net;java;php
高层协议-> FTP,SMTP
TcpListener
Server->
{
启动(开始监听端口) server.Start();
等待客户端连接 client = server.AcceptTcpClient();
建立网络数据流 stream = client.GetStream();
发送(写流) stream.Write()...
停止监听 server.Stop();
TcpClient()
{
建立连接(ip,port) client.Connect();
获取数据流 client.GetStream();
接收(都流) stream.Read();...
关闭 stream.Close();
client.Close();
System.Net System.Net.Sockets 命名空间-> 实现网络编程。
新建项1(控制台应用程序 客户端),新建项2(控制台应用程序 服务端)特点:
1.用到的命名空间多
2.方法调用先后非常严格
3.同时考虑两端【服务端、客户端】
static void Main(string[] args)
{
Console.WriteLine(“Server”);
//1.创建服务端通讯对象 监听对象
TcpListener server = new TcpListener(IPAddress.Any,9999);
//2.启动监听对象server。
server.Start();
//3.接受客户端连接请求
Socket socket = server.AcceptSocket();
if (!socket.Connected)
{
Console.WriteLine(“没有连上”);
return;
}NetworkStream stream = new NetworkStream(socket);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
//4.数据交换
//发
Console.WriteLine("请输入要发送的信息");
string sendLine = Console.ReadLine();
writer.WriteLine(sendLine);
//清空发送缓存
writer.Flush();
writer.Close(); reader.Close();
stream.Close(); socket.Close();
server.Stop();
static void Main(string[] args)
{
Console.WriteLine(“Client”);
//1.创建客户端对象
TcpClient client = new TcpClient();//2.连接服务器
client.Connect(IPAddress.Parse("172.31.117.208"),9999);
if (!client.Connected)
{
Console.WriteLine("没有连上");
return;
}
Socket socket = client.Client;
//网络流
NetworkStream stream = new NetworkStream(socket);
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
//收数据
string getInfo = reader.ReadLine();
Console.WriteLine("Get Message: " + getInfo);
Console.ReadKey();
writer.Close();reader.Close();
stream.Close();socket.Close();
client.Close();
}
day47 设计模式 P541
{
设计模式:
模式: 每一个模式描述了一个在我们周围不断重复发生的问题。
一套被反复使用,多数人知晓,经过分类编目的代码设计经验总结。
{
1.单例设计模式 (Singleton)
2.工厂设计模式(Factory) 工厂方法(Factory method) 抽象工厂(abstract Factory)
3.生成器设计模式(Bulider)
4. (不常见) 【Prototype 原型设计模式】a.单例模式:
问题: 一个类只能创建一个对象,且该对象全局共享。
练习: 用控制台写出一个单例模式
class SingletonSample
{
private static SingletonSample _instance = null;
private SingletonSample() { }
public static SingletonSample Instance {
get
{
if (_instance == null) _instance = new SingletonSample();
return _instance;
}
}
public void printHelloSin()
{
Console.WriteLine("Hello-Singlesingle");
}
}
b.简单工厂SimpleFactory 多选1 P546
问题: 客户在使用对象时,面临在多个类型中选择一个来创建对象,具体的类型可能有变化。
定义一个全局的工厂类,负责类型的选择和对象的创建初始化,从而实现创建和表示分离。
适用性: 当一个类不知道它所必须创建的对象的类的时候。
设计时多做工作,但给系统带来更大的复用性和可维护性。
使用: 1.创建父类产品以及所有的子产品。
练习: 创建专门生产饭的工厂
//吃的
abstract class Meal { }
class Noddle : Meal { }
class Rice : Meal { }
class Cake : Meal { }
static class MealChooseFactory
{
public static Meal ChooseRice(int cho)
{
Meal meal = null; //重点在这里。
switch (cho)
{
case 0:
meal = new Rice();
break;
case 1:
meal = new Noddle();
break;
case 2:
meal = new Cake();
break;
default:
meal = new Rice();
break;
}
return meal;
}
}
c.抽象工厂 AbstractFactory
问题: 多个类型中以系列化的方式创建对象。 类似一个KFC的套餐,搭配很多东西。
案例:P547
抽象工厂 学习一个技术方向(Java,C#)
{abstract private 学习1(语言);
abstract private 学习2(语法);
abstract private 学习3(STL);
}
具体工厂:继承抽象工厂
具体工厂1 : 学习Java
{
override private 学习1(Java);
...
}
具体工厂2 : 学习C#
{
...
}
练习: 抽象工厂 -> 生产坦克和战斗机
具体工厂 -> 生产中国或美国的
abstract class Tank { }
abstract class Plane { }
class ChineseTank : Tank { }
class AmericaTank : Tank { }
class ChinesePlane : Plane { }
class AmericaPlane : Plane { }
abstract class Arsenal //兵工厂 -》 抽象工厂
{
public string Nation; //带有属性,否则可以直接用接口
public abstract Tank ManfactureTank();
public abstract Plane ManfacturePlane();
}
class ChinaArsenal : Arsenal //属于某个组织的兵工厂 ->具体工厂1
{
public override Tank ManfactureTank()
{
return new ChineseTank();
}
public override Plane ManfacturePlane()
{
return new ChinesePlane();
}
}
class AmericaArsenal : Arsenal //属于某个组织的兵工厂 ->具体工厂2
{
public override Tank ManfactureTank()
{
return new AmericaTank();
}
public override Plane ManfacturePlane()
{
return new AmericaPlane();
}
}
调用:->
Arsenal arsenal = new AmericaArsenal();
Tank a = arsenal.ManfactureTank();
Plane b = arsenal.ManfacturePlane();
UML 类图绘制。
d.Builder 构建者,建造器
对象中的成员可能是其它类型的对象
复杂对象的结构是稳定的,但是组成结构的具体对象是变化的。
public class Computer {
public string Cpu;
public string Gpu;
public string Ram;
public string Power;
}
public interface ComputerBulider
{
void GetGpu();
void GetCpu();
void GetRam();
void GetPower();
Computer GetComputer(); //获取组装进度
}
public class Client //顾客 -> Director.
{
ComputerBulider computerBulider;
public Client(ComputerBulider computerBulider)
{
this.computerBulider = computerBulider;
}
public void Purchase()
{
computerBulider.GetCpu();
computerBulider.GetGpu();
computerBulider.GetRam();
computerBulider.GetPower();
}
}
public class ComputerBuliderChepMan : ComputerBulider
{
Computer product = new Computer();
public Computer GetComputer()
{
return product;
}
public void GetCpu()
{
product.Cpu = "i3-9100f";
Console.WriteLine("i3-9100f");
}
public void GetGpu()
{
product.Gpu = "Rtx-1080";
Console.WriteLine("Rtx-1080");
}
public void GetPower()
{
product.Power = "GreatWall";
Console.WriteLine("GreatWall");
}
public void GetRam()
{
product.Ram = "Kingston 2G";
Console.WriteLine("Kingston 2G");
}
}
public class ComputerBuliderExpensiveMan : ComputerBulider
{
Computer product = new Computer();
public Computer GetComputer()
{
return product;
}
public void GetCpu()
{
product.Cpu = "i10-10000k";
Console.WriteLine("i10");
}
public void GetGpu()
{
product.Gpu = "Rtx-3090";
Console.WriteLine("Rtx-3090");
}
public void GetPower()
{
product.Power = "Huntkey - 1000W";
Console.WriteLine("Huntkey-1000W");
}
public void GetRam()
{
product.Ram = "Huntkey - 1000W";
Console.WriteLine("Kingston 32G");
}
}
调用:
ComputerBulider bulider1 = new ComputerBuliderExpensiveMan();
Client client = new Client(bulider1);
client.Purchase();
var a = bulider1.GetComputer();
builder 模式和抽象工厂模式的区别:
builder侧重过程: builder要有相同的生产过程,且有部件需要生产
抽象工厂侧重生产结果。
2.结构型设计模式:
a. Facade(外观,门面) 关键词: 向导 P551
问题: 客户代码访问业务代码,需要使用多个业务对象才能实现
此时不能让客户来组织访问流程,而应该设置向导。
案例: SkillSystem.cs
b. Adapter 适配器模式: (例子: 电源适配器)
问题: 将一个类的接口转换成客户希望的另外一个接口。
1.创建适配器类 (电源适配器)
2.适配器继承目标类(Target-> 笔记本电脑)
3.适配器中实例化已有类,调用已有类的对象的方法。
public class PowerAdapter: Laptop
{
private socket220V = new socket220V();
override public void Charge(int a) //重写目标类充电方法
{
string b;
b = a.Tostring(); //这里解决a,b不兼容问题。
socket220V(b); //实现充电。
}
}
调用:
!! Laptop laptop = new PowerAdapter(); //声明父类new子类,调用重写方法。
Laptop.Charge(2);
练习: 台灯 (方法: 三个孔) 现有插座(方法: 两个孔)
转接器(继承台灯,字段adaptee,方法: 三个孔)
class DeskLamp { virtual public void ThreeSocket() { } }
class CurrentSocket { public void TwoSocket() { } }
class Adapter : DeskLamp //转接器
{
private CurrentSocket adaptee = new CurrentSocket();
public override void ThreeSocket()
{
/*这里实现接口转换*/
//...转换电压,或者地线去除。。。。
adaptee.TwoSocket();
}
}
调用:
static void Main(string[] args)
{
DeskLamp lamp = new Adapter();
lamp.ThreeSocket();
}
c. Bridge (桥): 关键词:连接两端变化
问题: 主体类有变化产生不同的派生,主体类成员也有变化
在组合关系上可能会形成排列组合,将两个变化分别独立
再利用关联建立主体与成员的关系。
这个关联就是桥
意图: 使抽象部分(主体成员) 与实现部分分离
abstract class Shape
{
public abstract void Draw();
}
class Circle : Shape
{
override public void Draw()
{
Console.Write("Circle");
}
}
class Rectangle : Shape
{
override public void Draw()
{
Console.Write("Rectangle");
}
}
abstract class DrawPosition
{
public Shape shape; //重点是这句话,这里实现了桥
virtual public void drawPosition()
{
shape.Draw();
}
}
class Papper : DrawPosition
{
public override void drawPosition()
{
Console.WriteLine("Papper->");
base.drawPosition();
}
}
class Blackboard : DrawPosition
{
public override void drawPosition()
{
Console.WriteLine("Blackboard->");
base.drawPosition();
}
}
调用:
static void Main(string[] args)
{
DrawPosition pos = new Papper();
pos.shape = new Rectangle();
pos.drawPosition();
}
d.Composite(组合) 关键词: 树
问题: 将对象组成树形结构。
将对象组成树形结构以表示"部分-整体"的层次结构。
Add(),Remove(),
GetChild(int),Insert(),DumpContent()展开
练习: 建立菜单树,实现树 P558
1.公司属于CEO管理
2.3个部门: 研发,市场,客服,都有部门经理
3.研发部:两个小组 ->服务端,客户端
服务端2人,客户端3人,市场2人,客服2人
每个员工类增加名字,薪水。
计算所有人员薪水之和
计算某部门或某小组的人员薪水和
//这题机械劳动太多了。。没写完,回头看看有没有更方便插入结点的方法,这里我自建了一个Insert方法和FindNode方法。
abstract class Component //树的组件,包括叶和结点
{
protected string Name;
public Component(string name)
{
Name = name;
}
public abstract int Add(Component component); //添加新的组件,可能是叶或结点
public abstract void DumpContent(); //下载内容
//其它功能
}
class Node : Component
{
public Node(string s) : base(s)
{ }
private ArrayList arrayList = new ArrayList();
public override int Add(Component component)
{
return arrayList.Add(component);
}
public override void DumpContent()
{
Console.WriteLine("Node -> {0}", Name);
foreach (Component component in arrayList)
{
component.DumpContent();
}
}
public Node FindNode(string Name)
{
if (this.Name == Name) return this;
Node fin=null;
for (int i = 0; i < this.arrayList.Count; i++)
{
if (arrayList[i].GetType() == typeof(Node))
fin = ((Node)arrayList[i]).FindNode(Name);
if (fin != null) return fin;
}
return null;
}
public bool Insert(string NodeName,Node node)
{
Node parentNode = FindNode(Name);
if (parentNode == null) return false;
parentNode.Add(node);
return true;
}
public bool Insert(string NodeName, Leaf leaf)
{
Node parentNode = FindNode(Name);
if (parentNode == null) return false;
parentNode.Add(leaf);
return true;
}
}
class Leaf : Component
{
public Leaf(string s) : base(s)
{ }
public override int Add(Component component)
{
throw new Exception("Leaf Can't add component");
}
public override void DumpContent()
{
Console.WriteLine("------------>Leaf{0}", Name);
}
}
class Employee : Leaf
{
public Employee(string Name,int salary) : base(Name) {
Salary = salary;
}
private int salary;
public int Salary { get
{
return salary;
}
set
{ salary = value; }
}
}
调用:
class Program
{
static void Main(string[] args)
{
Node root = new Node("Company");
buildTree(root);
root.DumpContent();
}
private static void buildTree(Node root)
{
Node node;
root.Add(new Node("CEO"));
node = root.FindNode("CEO");
node.Add(new Node("RandD"));
node.Add(new Node("Marketing"));
node.Add(new Node("CustomerService"));
node = node.FindNode("RandD");
node.Add(new Node("Server"));
node.Add(new Node("Client"));
node.Add(new Node("RandD-Manager"));
node.Add(new Leaf("Test"));
//...
}
//上面的方法很蠢。。。
//下面直接建立层级关系
private static void buildTree(Node root)
{
Node l11 = new Node("Company");
Node l21 = new Node("CEO");
Node l31 = new Node("RandD");
Node l32 = new Node("Marketing");
Node l33 = new Node("CustomerService")
;
Node l41 = new Node("Service");
Node l42 = new Node("Client");
Leaf m1 = new Employee("Manager1", 20000);
Leaf m2 = new Employee("Manager2", 20000);
Leaf m3 = new Employee("Manager3", 20000);
Leaf o1 = new Employee("officer1", 10000);
Leaf o2 = new Employee("officer1", 10000);
Leaf o3 = new Employee("officer1", 10000);
Leaf o4 = new Employee("officer1", 10000);
Leaf o5 = new Employee("officer1", 10000);
//添加层次关系
l11.Add(l21);
l21.Add(l31); l21.Add(l32); l21.Add(l33);
l31.Add(l41); l31.Add(l42);
l31.Add(m1);l32.Add(m2);l33.Add(m3);
l41.Add(o1); l41.Add(o2);
l42.Add(o3); l42.Add(o4); l42.Add(o5);
...
}
e.Decorator(装饰) 关键词: 包装 P560
问题 : 对一个对象动态扩展原有的行为能力,不断包装,不断扩展。
例子: 武器镶嵌不同宝石 获得不同方法/武器技能
abstract class Weapon
{
public abstract void Damage();
}
class Sword : Weapon
{
public override void Damage()
{
Console.WriteLine("Damage");
}
}
abstract class SwordDecorator : Weapon
{
public Weapon Weapon1 { get; set; } //剑的引用
public SwordDecorator(Weapon a) //装武器
{
Weapon1 = a;
}
public virtual Weapon RemoveDecorator()
{
return Weapon1;
}
public virtual void GetDecorator(){}
public override void Damage()
{
Weapon1.Damage();
}
}
class sapphire : SwordDecorator
{
public sapphire(Weapon a) : base(a)
{ }
public override void Damage()
{
base.Damage();
Frozen();
}
public override void GetDecorator()
{
base.GetDecorator();
//装上蓝宝石
}
public void Frozen() //冻结
{
Console.WriteLine("Frozen");
}
}
class Ruby : SwordDecorator
{
public Ruby(Weapon a) : base(a)
{ }
public override void Damage()
{
base.Damage();
Burn();
}
public override void GetDecorator()
{
base.GetDecorator();
//装上红宝石
}
public void Burn() //燃烧
{
Console.WriteLine("Burn");
}
}
调用
static void Main(string[] args)
{
Weapon weapon = new Ruby(new sapphire(new Sword())); //装蓝宝石+红宝石
weapon.Damage();
}
}
f. Proxy (代理) 关键词(幌子) P561
问题:为其它对象提供一种代理以控制对这个对象的访问
由小对象表示大对象(类似继电器)
(委托)
行为型-关注对象交互的问题 P561
a.Template method (模板方法) 关键词(骨架)
问题某功能实现的大流程已经确定了
但每一个步骤都可能 有不同的实现
一次性实现算法不变的部分,并将可变的
b.State(状态) 关键词状态决定行为 P564-565
在一个对象内部状态改变时行为发生改变
例子
通过反射动态创建 瓶子状态: 关闭,打开,工作
要求能方便转换
1.定义状态枚举。
2.定义状态抽象基类,实现状态子类(多个)
3.定义状态机->
{
1.要确定当前状态并初始化 CurrentState
2.定义状态切换的方法
3.调用当前状态的方法——》 pour。
4.根据需要定义其它成员。
}
abstract class State
{
public abstract void pour();
}
class BottleOpen : State
{
public override void pour()
{
Console.WriteLine("water out!");
}
}
class BottleClose : State
{
public override void pour()
{
Console.WriteLine("ERROR! Water Can't be pour!");
}
}
class BottleWork:State
{
public override void pour()
{
Console.WriteLine("pouring..");
}
}
class BottleState //状态机
{
public enum BottleStateenum
{
Open,
Close,
Work
}
private State CurrentState;
private State LoadState(BottleStateenum bottleState)
{
State state = null;
string stateName = "State.Bottle" + bottleState.ToString();
//string stateName = "State.Bottle" + bottleState.ToString().Split('.')[1];
Type typeobj = Type.GetType(stateName);
state = (State)Activator.CreateInstance(typeobj);
return state;
/*
switch (bottleState)
{
case BottleState.Open:
state = new BottleOpen();
return state;
case BottleState.Close:
state = new BottleClose();
return state;
case BottleState.Work:
state = new BottleWork();
return state;
default:
return null;
}*/
}
public BottleState()
{
CurrentState = LoadState(BottleStateenum.Close);
}
public void SetBottleState(BottleStateenum bottleState)
{
CurrentState = LoadState(bottleState);
}
public void Pour()
{
CurrentState.pour();
}
}
调用:
static void Main(string[] args)
{
BottleState bottleState = new BottleState();
bottleState.Pour();
bottleState.SetBottleState(BottleState.BottleStateenum.Open);
bottleState.Pour();
}
算法是多种实现,练习: 计算员工工资 程序员/测试,销售
程序员 工资: 底薪10000,补助1000,项目奖金10000
测试
销售 工资: 5000 补助1000 提成(销售总额*0.01)
abstract class StrategySalary
{
protected int baseSalary;
protected int subsidy;
public virtual int GetSalary()
{
return baseSalary + subsidy;
}
}
class ProgrammerStrategy : StrategySalary
{
protected int ProjectBonus;
public ProgrammerStrategy(int ProjectNum)
{
baseSalary = 10000;
subsidy = 1000;
ProjectBonus = 10000 * ProjectNum;
}
public override int GetSalary()
{
return baseSalary + subsidy + ProjectBonus;
}
}
class TesterStrategy : StrategySalary
{
protected int findBugsSalary;
public TesterStrategy(int BugNums)
{
baseSalary = 10000;
subsidy = 1000;
findBugsSalary = 100 * BugNums;
}
public override int GetSalary()
{
return baseSalary + subsidy + findBugsSalary;
}
}
class SalesManStrategy : StrategySalary
{
protected int Commission; //提成
public SalesManStrategy(int SaleVolumns)
{
baseSalary = 10000;
subsidy = 1000;
Commission = (int)(0.01 * SaleVolumns);
}
public override int GetSalary()
{
return baseSalary + subsidy + Commission;
}
}
class Employee
{
protected string job;
StrategySalary strategy;
public Employee(StrategySalary strategy)
{
this.strategy = strategy;
}
public int GetSalary()
{
return strategy.GetSalary();
}
}
调用:
static void Main(string[] args)
{
ProgrammerStrategy strategy = new ProgrammerStrategy(3);
Employee a = new Employee(strategy);
Console.WriteLine(a.GetSalary());
}
d. observe (观察者模式) 关键词: 事件模式
class Cat
{
public delegate void CatBarkHandler();
public event CatBarkHandler CatBarkEvent; //声明事件
public void CatBark()
{
Console.WriteLine("miao~~");
if (CatBarkEvent != null)
{
CatBarkEvent(); //调用事件
}
}
}
class Mouse
{
public void Run()
{ Console.WriteLine("Mouse Run"); }
}
调用:
static void Main(string[] args)
{
Cat cat = new Cat();
cat.CatBark();
Console.WriteLine("____________________________");
cat.CatBarkEvent += new Mouse().Run;
cat.CatBark();
}
{
主角攻击小怪,妖怪要有一些可能性的反应-> 有限状态机
1.待机
2.死亡
3.追逐
4.攻击
5.巡逻在本状态作为当前状态时,还要做状态检查(发现目标,生命为0)。
若发现状态改变条件满足->调用状态机改变状态方法。
状态机-> 包含AI 所有状态的容器。 ->状态对象的初始化,负责状态的切换,实时执行当前状态的行为以及条件检查。
默认状态 状态编号 条件编号
当前状态 条件列表(该状态下要进行的检测)
为状态类或条件类提供的数据 转换映射表方法
状态机管理 添加条件 bool检测条件是否达成
为状态类或条件类提供行为 查找映射
状态行为
条件检测
离开状态
进入状态
条件类(史莱姆生命存活条件类,史莱姆索敌条件类)
Common/FSMStateID ID状态编号枚举
/FSMTriggerID ID条件编号枚举
/AnimationParameters 动画参数类 //添加抽象条件类 FSMTrigger
//添加抽象状态类 FSMStatus
P569
ConfigSM(){
1.创建状态对象
2.添加条件映射 AddTrigger
3.放入状态集合=状态库
}
1.硬编码-> 量大,难修改,代码复用性差2.动态编码-> 方法,方便
创建Fsm配置文件
Assets 包含规定好的文件夹:
大部分只读
可读可写文件夹: StreamingAssests
步骤:
a. 创建文件夹StreamingAssests
b. 添加状态配置文件 编写配置文件
c. 添加读取配置文件的类
d. 在状态机的ConfigFSM方法中使用!反射技术实现功能
{
1.创建状态对象
2.添加条件映射 AddTrigger
3.放入状态集合=状态库
}
例子: -> 基于程序集反射实现
private void ConfigFSM()
{
//使用动态编码方式,实时加载Application.streamingAssetsPath + "/AI_StatusConfig.txt"
Dictionary