unity学习总结
最近学了下Unity,先跟官方教程走了个3D碰撞球的小游戏的教程。然后实现了“合成同学小游戏“,借鉴合成大西瓜的游戏机制。
Unity介绍
unity是一个游戏引擎,帮助快速开发游戏。个人觉得unity的一大优势是跨平台编译(虽然其它平台也可能有跨平台编译),比如”合成同学小游戏“是2D休闲类游戏,第一需求是就是便捷性,因此web是最合适的平台。
Unity WebGL编译
unity WebGl编译可以直接产出js、data、css、html、wasm和图片使游戏可以直接运行在web端。
在编译对象的时候有三种压缩方式br、gzip和不压缩。br是google提出的web内容压缩格式压缩率最高也是unity默认的格式。我摸索的上线模式就是把这些生成的内容以http文件传输的方式提供给客户端访问。既然br(Brotli)的压缩率最高自然而然就作为了第一选择,但我在使用过程中遇到了一些问题,最终选择了更稳定的gzip,下面讲一下具体原因。
safari使用不灵?使用br压缩方式遇到的问题
因为上线服务的方式就是把编译出的js、data、css、html放到服务器服务,这些编译内容以index.html为核心组织,客户端GET domain:port/index.html后index.html中的js代码会在客户端浏览器执行对其它js、data、css资源进行GET请求。当所有内容正确在客户端载入时游戏就可以玩了。
但对于内容的压缩格式,在http协议中要客户端和服务端要对压缩格式进行约定。
1 | 客户端先发起请求在Header中添加: |
1 | 服务端在返回的Header中添加: |
但我在使用python写了一版br http服务端代码后发现window上能正常运行,但在Safari上客户端的http请求Accept-encoding没有br),怀疑是兼容性问题,遂搁置问题转向gzip。
Unity editor
“下面来聊聊unity本身”
unity editor本身由六大部分组成
1.hierarchy
2.project
3.console
4.game
5.scene
6.inspector
从游戏角度看组织结构如下:
scene
gameObject1
component1(transform)
component2(rigidbody)
component3(collier)
component4(C# script)
gameObject2
gameObject2
unity中的类和实例
类和实例是在编程中一个常见的概念关于类和实例:
1.在代码中通过Instantiate()函数实例化(Instantiate中传入的也是实例,Instantiate返回对该实例的拷贝)
2.在unity中hierarchy中的内容都是实例,
3.C#脚本中声明并在inspector中拖拽赋值的实例变量
此外GameObject是类,gameObject是实例
刚体和碰撞
刚体(rigidbody)该component给游戏对象增加物理属性,如重力重量
碰撞(collider)只有两个游戏对象都启动这个component时才会检测到碰撞,碰撞分为两类
1.Collision,造成物理碰撞,可以在碰撞时执行OnCollision函数。
OnTriggerEnter
2.Trigger,取消所有物理碰撞,可以在碰撞时执行OnTrigger函数。
OnTriggerEnter、OnTriggerStay、OnTriggerExit
这两种方式通过在inspector中点击is trigger切换
实例间的通信
实例间的通信方法
1.A调用B的情况:
1.1 GameObject.Find(“B”)得到所有名为B的实例的脚本对象通过其访问函数进行访问
1.2 或者在Fruit脚本中增加 public Game;并将Game实例拖拽过去dd
1.3 将B中要调用的内容使用static预实例化
2.A实例内产生B的情况:
因为实例在脚本中instancelize 因此可以直接对该实例操作
遇到的其他问题
1.对象碰撞逻辑重复执行导致陷入死循环
问题描述:因为游戏逻辑是两个相同对象碰撞后合成一个更大的对象,但碰撞发生时两个对象都调用OnTriggerEnter2D分别产生的一个更大的对象,两个更大的对象又彼此碰撞无限循环
解决办法:在碰撞发生时通过object.GetInstanceID(),比较两个对象的id只有id更小的才能执行合并操作。
2.A和B、C同时碰撞
预期 实际
问题描述:A和B、C同时碰撞生成了两个更大的对象,但预期是只能和其中一个发生碰撞合成一个更大的对象
解决办法:使用static建立了一个hash,key为实例id,在合并前先查看实例是否存在。合并在该hash中移除相应实例id
Good Soul
祁同伟有一个优良的生活习惯:每天早晨六点半,准时来到健身房,把各种器械练一遍。七点二十分结束锻炼,冲一个凉水澡,到隔壁粤式茶楼吃早餐,然后坐接他的奥迪专车去公安厅上班。这么早去健身房有些不可思议,但他只有如此才能保证锻炼时间。身为公安厅厅长,他白天忙得抽不出空,晚上又要接待应酬,还常常开会,加班处理突发案件
《人民的名义》
Linux中的定时任务
最近常遇到需要在linux系统中一直启动一个服务、或定时启动某服务的场景。
最简单的在用ssh连接启动程序,但因为程序是从ssh shell中fork出的子进程,程序会随着ssh的中断而中断。因此调研的两种方法
crontab
crontab轻量,不需要太多配置
1 | crontab -e #cron编辑的文件以用户为单位存放在 /var/spool/ |
crontab中任务的格式为
cron时间
命令
如每分钟向日志中写入”test”
1 | * * * * * echo "test" >> ~/1.log |
cron时间是一个类似于正则的表达可以在这里查看https://crontab.guru/
systemd
systemd更“重”一点,优点是自动记录log、通过配置.service文件就可以传入环境变量给进程。但配置.service文件比较麻烦
systemd是一个linxu服务管理工具,面向进程。系统的init进程就是它创建的。
ps -ef
查看:
操作步骤
在/etc/systemd/system中添加service配置文件mytest.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22[UNIT]
#服务描述
Description=Browser Preview Service
#指定了在systemd在执行完那些target之后再启动该服务
After=network.target
[Service]
#定义Service的运行类型
Type=simple
#定义systemctl start|stop|reload *.service 的执行方法(具体命令需要写绝对路径)
#ExecStartPre为启动前执行的命令
#项目中有相对路径时要指定工作目录
WorkingDirectory=/root/http_server
ExecStart=/root/http_server/main 8090
ExecReload=
#MAINPID是systemd记录的主进程PID
Restart=always
ExecStop=/bin/kill -9 ${MAINPID}
[Install]
#多用户
WantedBy=multi-user.target查看service状态
1
systemctl status mytest.service
启动service
1
systemctl start mytest.service
停止service
1
systemctl stop mytest.service