Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Can';t更改衍生游戏对象的精灵(仅限客户端)_C#_Unity3d_Sprite_Unity3d Unet - Fatal编程技术网

C# Can';t更改衍生游戏对象的精灵(仅限客户端)

C# Can';t更改衍生游戏对象的精灵(仅限客户端),c#,unity3d,sprite,unity3d-unet,C#,Unity3d,Sprite,Unity3d Unet,我试图在Unity3D中实例化、生成一个精灵,然后将其分配给一个自定义的游戏对象。这些对象是一个通用的CardContainer,它调用SetCard方法为其提供自定义统计信息。调用SetCard也会将CardContainer分配给它的精灵 我的问题是,每当我更改生成的游戏对象的spriteender.sprite时,sprite更改不会对客户端实例产生影响 它似乎也没有反映在生成对象之前是否对精灵进行了任何更改。这可以改变雪碧吗?我怎么做 我已经设置了一些小的poc测试,但到目前为止没有任何

我试图在Unity3D中实例化、生成一个精灵,然后将其分配给一个自定义的
游戏对象。这些对象是一个通用的
CardContainer
,它调用
SetCard
方法为其提供自定义统计信息。调用
SetCard
也会将
CardContainer
分配给它的精灵

我的问题是,每当我更改生成的
游戏对象
spriteender.sprite
时,sprite更改不会对客户端实例产生影响

它似乎也没有反映在生成对象之前是否对
精灵
进行了任何更改。这可以改变雪碧吗?我怎么做

我已经设置了一些小的poc测试,但到目前为止没有任何效果。这是:

//cardContainerTesting
Vector3 testingContainerCoords= new 
Vector3(0, 1, -1);
GameObject testingCardObjectInstance = Instantiate(testingCardCOntainerGameObject, testingContainerCoords, Quaternion.identity); 
NetworkServer.Spawn(testingCardObjectInstance);
SpriteRenderer objectSprite = testingCardObjectInstance.GetComponent<SpriteRenderer>();
objectSprite.sprite = testingSprite1;


//GenericGameObjectExampleTesting
Vector3 origin = new Vector3(0, 0, -1);
GameObject instantiatedPrefab = Instantiate(myPrefabExample, origin, Quaternion.identity);
NetworkServer.Spawn(instantiatedPrefab);
SpriteRenderer exampleSpriteRenderer = instantiatedPrefab.GetComponent<SpriteRenderer>();
exampleSpriteRenderer.sprite = testingSprite2;
//cardContainerTesting
Vector3 testingContainerCoords=新建
向量3(0,1,-1);
GameObject testingCardObjectInstance=实例化(testingCardCOntainerGameObject、testingContainerCoords、Quaternion.identity);
NetworkServer.Spawn(testingCardObjectInstance);
spriteender objectSprite=testingCardObjectInstance.GetComponent();
objectSprite.sprite=testingSprite1;
//GenericGameObjectExampleTesting
向量3原点=新向量3(0,0,-1);
GameObject InstancedPrefab=实例化(myPrefableExample,origin,Quaternion.identity);
NetworkServer.Spawn(实例化预制);
SpriteRender示例SpriteRender=实例化预制.GetComponent();
示例spriterenderer.sprite=测试sprite2;

事实上,这相当棘手,取决于你的情况

最好的情况是事先知道哪些精灵可用,并将其存储在
列表中。。然后,您可以通过在生成的对象上设置一个
[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];
    }
}
现在,目标精灵对象用正确的精灵初始化自身

另外,使用
钩子
现在,每当服务器上的索引更改时,它实际上会更改。因此,现在您甚至可以在运行时动态切换精灵,只需分配一个新索引:

private void Update()
{
    if(!isServer || !Input.GetKeyDown(KeyCode.ArrowUp)) return;

    SpriteIndex = (SpriteIndex + 1) % Sprites.Count;
}


另一种方法可能包括传输实际的
纹理2d
数据。这有点棘手,因为通过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;
}
//我们要转移的精灵
公共精灵目标精灵;
//产卵的预制场
//在这里直接使用组件类型,这样我们就不用调用一个GetComponent
公共喷灯示例预制件;
[命令]
public void Cmd_Spawn()
{
//在服务器上
var obj=实例化(ExamplePrefact);
//在服务器上,立即设置精灵
对象精灵=目标精灵;
//产卵(精灵尚未设置)
NetworkServer.Spawn(obj.gameObject);
//告诉客户端设置精灵并传递所需数据
Rpc_afterpawn(obj.gameObject,targetSprite.texture.EncodeToPNG(),newspriteinfo(targetSprite));
}
[可序列化]
私有结构SpriteInfo
{
公共Rect;
公共向量2支点;
公共精灵信息(精灵精灵精灵)
{
rect=sprite.rect;
pivot=sprite.pivot;
}
}
[客户端RPC]
私有void Rpc_afterpawn(游戏对象targetObject,字节[]纹理数据,SpriteInfo SpriteInfo)
{
//关于客户
//初始宽度和高度无关紧要
//它们将被加载覆盖
//此外,jpg数据的纹理格式将自动为RGB24
//png为RGBA32
var纹理=新纹理2d(1,1);
//将字节[]加载到纹理中
纹理。加载图像(纹理数据);
var newSprite=Sprite.Create(纹理、spriteInfo.rect、spriteInfo.pivot);
//最后在所有客户端上设置精灵
targetObject.GetComponent().sprite=newSprite;
}

但是请注意

  • 这也是非常有限的,因为UNet只允许64kb的网络缓冲区,所以任何较大的图像/纹理(+其余数据!)都不可能以这种方式传输,而且会变得更复杂

    在这方面还要注意,
    ency
    通常会导致数据大小大于原始图像

  • 我现在也不确定
    Spawn
    Rpc_afterpawn
    的执行顺序在网络上是否可靠。可能发生的情况是,
    Rpc_afterpawn
    Spawn
    实际完成之前到达客户端


您是否事先知道可能的精灵,以便将它们存储在列表或字典中?我的所有精灵都存储在我的“SpriteCollection”字典中,可以通过我的“SetCard”功能从和CardContainer中收集。在我尝试多人游戏之前,它一直工作得很好。在我的另一条评论中,我的角色已经用完了,但我只想感谢大家。你的帖子写得很彻底,一定花了一段时间。我真的很感激!非常感谢。在这个问题上,我尴尬地被困了很长时间。我今天一直没能按照你的指示去做。我已将您的代码一字不差地粘贴到我的项目中。我现在得到一个精灵出现在我的客户,但不是正确的一个。出现的精灵是我的“精灵”列表中的第0项,而不是我输入的第一项或任何其他索引。它是在主机中找到正确的精灵。有什么想法吗?在我看来,SpriteIndex似乎没有被正确地共享。我将你的启动函数代码添加到更新函数中,以确保它运行,但仍然无效。嘿,对不起。。不确定了,因为UNet是很久以前的事了。。。我非常确定它使用
Start
工作,而且在调用
Start
之前,应该在Spawn之后同步
[SyncVar]
。这对我也很管用。。但可能这是一个运行时“bug”,只对我有效,因为我在同一台PC上进行测试。然而,我更新了代码,改为使用
钩子
[ClientC]
// 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;
}