0%

image-20220428182612200

最近学了下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
2
3
客户端先发起请求在Header中添加:
Accept-encoding:br, gz
代表接受的压缩编码格式
1
2
3
服务端在返回的Header中添加:
content-type:application/js 或其他的text/css
Content-encoding: br

但我在使用python写了一版br http服务端代码后发现window上能正常运行,但在Safari上客户端的http请求Accept-encoding没有br),怀疑是兼容性问题,遂搁置问题转向gzip。

https://github.com/Bigpop/Unity-webGL-Server.git

Unity editor

“下面来聊聊unity本身”

unity editor本身由六大部分组成

1591643009468_.pic_hd

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同时碰撞

1581643009463_.pic_hd 预期1571643009462_.pic_hd 实际1561643009462_.pic_hd

问题描述:A和B、C同时碰撞生成了两个更大的对象,但预期是只能和其中一个发生碰撞合成一个更大的对象

解决办法:使用static建立了一个hash,key为实例id,在合并前先查看实例是否存在。合并在该hash中移除相应实例id

祁同伟有一个优良的生活习惯:每天早晨六点半,准时来到健身房,把各种器械练一遍。七点二十分结束锻炼,冲一个凉水澡,到隔壁粤式茶楼吃早餐,然后坐接他的奥迪专车去公安厅上班。这么早去健身房有些不可思议,但他只有如此才能保证锻炼时间。身为公安厅厅长,他白天忙得抽不出空,晚上又要接待应酬,还常常开会,加班处理突发案件

《人民的名义》

最近常遇到需要在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 查看:

image-20211224174422781

操作步骤

  1. 在/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
  2. 查看service状态

    1
    systemctl status mytest.service
  3. 启动service

    1
    systemctl start mytest.service
  4. 停止service

    1
    systemctl stop mytest.service

icon