Unity在很多需求下,不得不进行颜色的像素级填充,
诸如:实时贴花功能,绘图功能开发,乃至非Native的情况下视频播放(比如视频是CPU计算出来的,而不是文件流或直播流,提交颜色),各种游戏模拟器,等画面显示;
亦或者,必须要通过CPU逻辑运算,得到一个图形,并展示到UI中,或某个贴图上时,
采用类似Rendertexture的方式,都就需要处理Textture2d的填充。
尤其在高频填充的情况下(指60帧或者更高刷新率绘制),我们就需要考虑效率了。
以下是我做Unity下街机模拟器画面显示开发的心得
* 最终我还要上PSVita,使用unity(113M的极限内存可用,还要预留性能给我的网络联机网络库,害只能用77M的模式,主频非超频也只有333Mhz,所以能省则省了)
省流版,总结,几种:
Plan1. 使用SetPixelData< T > (T[] data, int mipLevel, int sourceDataStartIndex = 0)
Plan2. GetNativeTexturePtr纹理指针,对底层指针,进行绘制。如果你对DX11/DX9的绘制指针比较了解 ,可以使用Texture2d的GetNativeTexturePtr 获取指针,在C++扩展下进行操作。(跨平台不友好)
Plan3. 低版本Unity使用 LoadRawTextureData< T >(NativeArray data),LoadRawTextureData(byte[] data)
Plan4. 低版本Unity使用 LoadRawTextureData 的指针用法 (推荐) LoadRawTextureData(IntPtr data, int size)
Plan5. 交给shader
//这里是一个代码构建的Texture2D用于填充数据和提交
private Texture2D m_rawBufferWarper;
//这里是UI上一个RawImage组件作为显示
private RawImage m_drawCanvas;
public enum AnchorPresets
{
TopLeft,
TopCenter,
TopRight,
MiddleLeft,
MiddleCenter,
MiddleRight,
BottomLeft,
BottonCenter,
BottomRight,
BottomStretch,
VertStretchLeft,
VertStretchRight,
VertStretchCenter,
HorStretchTop,
HorStretchMiddle,
HorStretchBottom,
StretchAll
}
/// <summary>
/// 安全的设置锚点,并保证位置不变
/// </summary>
/// <param name="source"></param>
/// <param name="allign"></param>
public static void SetAnchor(this RectTransform source, AnchorPresets allign)
{
//计算原锚点中心和组件大小
Vector2 oldAnchorCenter = (source.anchorMin + source.anchorMax) / 2;
Vector2 oldAnchoredPos = source.anchoredPosition;
Vector2 objSize = new Vector2(source.rect.width, source.rect.height);
switch (allign)
{
case (AnchorPresets.TopLeft):
{
source.anchorMin = new Vector2(0, 1);
public class ctrl : MonoBehaviour
{
[SerializeField]
PostProcessVolume m_postpVol;
[SerializeField]
Camera m_Cam;
DepthOfField m_Df;
void Awake()
{
m_postpVol.profile.TryGetSettings(out m_Df);
}
//焦距设置为摄像机和玩家的位置
void Update()
{
m_Df.focusDistance.value = Vector3.Distance(Player.position, m_Cam.transform.position);
}
}
将unity缓存三个文件夹
C:\Users\你用户名\AppData\Local\Unity
C:\Users\你用户名\AppData\LocalLow\Unity
C:\Users\你用户名\AppData\Roaming\Unity
分别拷贝到 别的盘符,如
F:\UnityCache\LocalUnity
F:\UnityCache\LocalLowUnity
F:\UnityCache\RoaMingUnity
删除源文件夹,添加软链接
mklink /j C:\Users\你用户名\AppData\Local\Unity F:\UnityCache\LocalUnity
mklink /j C:\Users\你用户名\AppData\LocalLow\Unity F:\UnityCache\LocalLowUnity
mklink /j C:\Users\你用户名\AppData\Roaming\Unity F:\UnityCache\RoaMingUnity
管理员身份运行CMD,并执行, 结果:
Microsoft Windows [版本 10.0.19044.1706]
(c) Microsoft Corporation。保留所有权利。
C:\Users\Admin>mklink /j C:\Users\Admin\AppData\Local\Unity F:\UnityCache\LocalUnity
为 C:\Users\Admin\AppData\Local\Unity <<===>> F:\UnityCache\LocalUnity 创建的联接
C:\Users\Admin>mklink /j C:\Users\Admin\AppData\LocalLow\Unity F:\UnityCache\LocalLowUnity
为 C:\Users\Admin\AppData\LocalLow\Unity <<===>> F:\UnityCache\LocalLowUnity 创建的联接
C:\Users\Admin>mklink /j C:\Users\Admin\AppData\Roaming\Unity F:\UnityCache\RoaMingUnity
为 C:\Users\Admin\AppData\Roaming\Unity <<===>> F:\UnityCac
我写一个简单明了的范例
比如我们要同步一个V3类型的数据,
数据来源:客户端操作
目标:同步所有客户端的 Vector3 V3FromNetwork 变量值
本地客户端客户端(isLocalPlayer为true)进行操作
↓↓↓
调用[Command]修饰的方法SetTestV3,(SetTestV3就具备了往服务器上报的特性)
↓↓↓
服务器执行SetTestV3函数内容
↓↓↓
服务器修改TestV3值时,由于具备标识[SyncVar(hook = nameof(ChangeTestV3))],通知所有客户端的该对象脚本中调用ChangeTestV3
↓↓↓
客户端调用ChangeTestV3,最终成功修改到值。
代码如下
using UnityEngine;
using Mirror;
public class Player : NetworkBehaviour
{
void Update()
{
if (!isLocalPlayer) return;//只操作本地客户端
Vector3 V3SendData = new Vector3(Input.GetAxis("Vertical") * Time.deltaTime, 0, Input.GetAxis("Horizontal") * Time.deltaTime);
SetTestV3(V3SendData);
}
[Command]//客户端上行标识
public void SetTestV3(Vector3 v)
{
//服务端执行内容
TestV3 = v;
}
[SyncVar(hook = nameof(ChangeTestV3))]//服务端修改触发客户端调用hook函数
public Vector3 TestV3 = Vector3.zero;
public void ChangeTestV3(Vector3 oldv, Vector3 newc)
{
//客户端执行内容
标签: Command 从客户端发送到服务器,服务器执行
[Command]
void ABC(){}
注意点
1,方法可以带参数,但需要序列化
2,客户端只能发送属于自己的对象的方法
如果都允许,则设置为不验证:
[Command(requiresAuthority = false)]
标签: ClientRpc 从服务器发送到,客户端执行
[ClientRpc]
void ABC(){}
标签: TargetRpc 从服务器发送到,指定的客户端执行
[TargetRpc]
void TargetRemoteDamaged(NetworkConnection target, int damage){}
PS:第一个参数可忽略,默认发给自己,也就是调用此方法的客户端实体
PS:如果第一个参数有,则发给指定,目标
[Command] 由客户端调用,内容服务器执行;
[ClientRpc]和[TargetRpc]由服务器调用,内容客户端执行;
但[TargetRpc]是指定目标发送,[ClientRpc]是都发送。
一,用SyncVar进行同步
函数标签[SyncVar]
用标签来标记变量,被改变时时,同步给其他客户端;
可指定Hook指定回调函数,并返回旧值和新值
如:
[SyncVar(hook = nameof(ChangeColor))]
Color pcolor = Color.white;
void ChangeColor(Color oldc, Color newc)
{
}
即pcolor发生变化时,客户端都调用ChangeColor
PS:使用此特性标注的变量,只应该在服务器上对此变量进行更改,而通过hook在客户端作用此更改
然后hook,仅会再客户端调用,如果服务器是OnlyServer模式则不会调用
使用脚本让服务器控制所有客户端颜色变化的代码
using UnityEngine;
using Mirror;
public class PlayerController : NetworkBehaviour
{
[SyncVar(hook = nameof(ChangeColor))]
Color pcolor = Color.white;
Rigidbody rb;
MaterialPropertyBlock prop;
/// <summary>
/// 客户端收到同步时调用
/// </summary>
/// <param name="oldc"></param>
/// <param name="newc"></param>
void ChangeColor(Color oldc, Color newc)
{
Debug.Log("更新为新颜色");
prop.SetColor("_Color", newc);
GetComponent<Renderer>().SetPropertyBlock(prop);
}
private void Awake()
{
rb = GetComponent<Rigidbody>();
prop = new MaterialPropert
个人理解的两种方式
Offline,Room,GamePlay
作用:区分Online和非Online模式
通过create->mirror 创建如下脚本
[NetworkRoomManager]
作用:房间管理,有房间创建,连接,等房间相关的回调
基于房间补充相关逻辑
[NetworkRoomPlayer]
作用:包含房间里的玩家状态和回调
[NetworkBehaviour]
创建多个NetworkBehaviour :
如,NetworkBehaviour 改为 myPlayer
NetworkBehaviour 改为 myItem
作用: 有了本脚本,才会标记告知服务器需要同步本对象
继承了MonoBehaviour的Network脚本,
可以衔接Update等函数书写自己的逻辑,
取到玩家网络状态,如
isServer
如果是此对象是在服务器上运行的(包括服务器+客户端一体的主机),则返回true
isServerOnly
如果是此对象仅在服务器运行的(而非包含客户端的主机),则返回true
isClient
如果此对象已经存在,且处于客户端上(包括主机的客户端),则返回true
等等
创建空物体,命名为RoomPlayer,挂MyNetworkRoomPlayer
创建胶囊,命名为Player,挂MyPlayer
创建Cube, 命名为Item 挂myItem
客户端操作权限:
Player预制体上的挂NetworkTransform脚本的ClientAuthority 一定要勾选上,否则没有客户端操作其状态的权限。
在玩家或者我物体预制体上挂载,NetworkIdentity 脚本
(往往挂载其他Mirror脚本时,NetworkIdentity脚本会自动挂载)
1.在Offline场景空物体,挂MyNetworkRoomManager脚本
挂载好在线和离线对应的场景,Player预制体,以及房间相关的预制体和场景
2.同时
比起 Unity集成的Navigation中的OffMeshLink,扩展的NavMeshComponents中NavMeshLink,会强大好用许多,
·比起 Unity集成的Navigation ,扩展的NavMeshComponents,扩展了不少方便的功能
比如同一个场景,更方便的创建不同的烘培网格
拷贝Github克隆的Asset中NavMeshComponents目录到你的项目Asset
创建一个场景,
然后在场景根节点下 挂载脚本提供的NavMeshSurface.cs
而且可以挂载多个
打开Agent 设置
设置不同体型的宽高配置
然后回到地形对象的Inspector面板,选择不同的配置
分别点击Bake,进行烘培得到两个不同的结果
此外还可以选择渲染的layer层级,
或者你也可以选择渲染范围:
然后就是移动了
创建两个玩家胶囊提
分别创建寻路导航代理组件
挂载移动脚本到两个player胶囊提上
public class Player : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
private NavMeshAgent agent;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
agent.SetDestination(hit.point);
}
}
}
}
鼠标控制移动,两个角色则会走对应体型配置的烘培寻路