我试图实例化,生成,然后将sprite分配给Unity3D中的自定义GameObject
。这些对象是通用的CardContainer
,它们调用SetCard
方法来为其提供自定义统计信息。调用SetCard
还会为CardContainer
分配其Sprite。
我的问题是,每当更改衍生的SpriteRenderer.sprite
的{{1}}时,sprite更改都不会影响客户端实例。
它似乎也不能反映我是否在生成对象之前对GameObject
进行了任何更改。这有可能改变精灵,我该怎么办?
我已经设置了一些小型poc测试,但到目前为止没有任何效果。他们在这里:
Sprite
答案 0 :(得分:1)
实际上,这很棘手,取决于您的情况。
最好的情况是,您事先知道可用的Sprite并将其存储,例如然后在List<Sprite>
中。通过设置例如产生的对象上的[SyncVar]
。像
// on the spawned object
public class SpriteController : NetworkBehaviour
{
// Also good if you reference this already in the Inspector
[SerializeField] private SpriteRenderer spriteRenderer;
// Configured via the Inspector befrorehand
public List<Sprite> Sprites;
// Whenever this is changed on the Server
// the change is automatically submitted to all clients
// by using the "hook" it calls the OnSpriteIndexChanged and passes
// the new value in as parameter
[SyncVar(hook = nameof(OnSpriteIndexChanged))] public int SpriteIndex;
// Will be called everytime the index is changed on the server
[ClientCallback]
private void OnSpriteIndexChanged(int newIndex)
{
// First when using a hook you have to explicitly apply the changed value at some point
SpriteIndex = newIndex;
if (!spriteRenderer) spriteRenderer = GetComponent<SpriteRenderer>();
spriteRenderer.sprite = Sprites[SpriteIndex];
}
}
然后例如
// If you make this of type SpriteController the Inspector automatically
// references the correct component and you can get rid of the GetComponent call later
public SpriteController testingCardCOntainerGameObject;
var testingCardObjectInstance = Instantiate(testingCardCOntainerGameObject, testingContainerCoords, Quaternion.identity);
// for testing use 1 since 0 is the default for int ;)
testingCardObjectInstance.SpriteIndex = 1;
NetworkServer.Spawn(testingCardObjectInstance);
现在,目标精灵对象会使用正确的精灵进行初始化。
现在另外使用hook
进行更改,每次,在服务器上更改索引。因此,现在您甚至可以通过简单地分配一个新索引来在运行时动态切换Sprite:
private void Update()
{
if(!isServer || !Input.GetKeyDown(KeyCode.ArrowUp)) return;
SpriteIndex = (SpriteIndex + 1) % Sprites.Count;
}
一种替代方法可能包括传输实际的Texture2D
数据。这有点棘手,因为通过UNet传递的允许的参数/数据类型非常有限
// the sprite we will transfer
public Sprite targetSprite;
// the prefab to spawn
// directly use the component type here so we get rid of one GetComponent call
public SpriteRenderer examplePRefab;
[Command]
public void Cmd_Spawn()
{
// ON SERVER
var obj = Instantiate(examplePRefab);
// on the server set the sprite right away
obj.sprite = targetSprite;
// spawn (sprite will not be set yet)
NetworkServer.Spawn(obj.gameObject);
// tell clients to set the sprite and pass required data
Rpc_AfterSpawn(obj.gameObject, targetSprite.texture.EncodeToPNG(), new SpriteInfo(targetSprite));
}
[Serializable]
private struct SpriteInfo
{
public Rect rect;
public Vector2 pivot;
public SpriteInfo(Sprite sprite)
{
rect = sprite.rect;
pivot = sprite.pivot;
}
}
[ClientRpc]
private void Rpc_AfterSpawn(GameObject targetObject, byte[] textureData, SpriteInfo spriteInfo)
{
// ON CLIENTS
// the initial width and height don't matter
// they will be overwritten by load
// also the texture format will automatically be RGB24 for jpg data
// and RGBA32 for png
var texture = new Texture2D(1, 1);
// load the byte[] into the texture
texture.LoadImage(textureData);
var newSprite = Sprite.Create(texture, spriteInfo.rect, spriteInfo.pivot);
// finally set the sprite on all clients
targetObject.GetComponent<SpriteRenderer>().sprite = newSprite;
}
但是请注意:
这也非常有限,因为UNet仅允许afaik 64kb的NetworkBuffer,所以任何更大的图像/纹理(+其余数据!)将无法以这种方式进行传输,并且会变得更加复杂。
在这方面还请注意,EncideToXY
的数据大小通常比原始图像大bigger。
我现在还不确定Spawn
和Rpc_AfterSpawn
的执行顺序在网络上是否可靠。在实际完成Rpc_AfterSpawn
之前,Spawn
可能会到达客户端。