Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Fikule · Jan 08, 2017 at 10:05 AM · networkingmultiplayercommandauthoritative

Send Commands from an Object in Multiplayer

Basically I have this:

 public class ShipDoor : UsableObject // UsuableObject inherits from NetworkBehavior
 {
     [SyncVar]
     bool isOpen = false;
     [SyncVar]
     float currentTransition = 0;
 
     public override void Use()
     {
         CmdUseDoor();
         Debug.Log("Opening " + m_name);
     }
 
     [Command]
     void CmdUseDoor()
     {
         Debug.Log("Opening(Server) " + m_name);
         isOpen = !isOpen;
         currentTransition = 0;
     }
 }

When a player clicks an object he gets a small dialog with a list of commands, when he clicks the command it is called on the object and the object will do whatever it needs to in the [Command].

However, since my client has no authority over this object he can't call the [Command]. How can I allow this? There will be many objects with various commands all doing different things. It would feel a bit messy to cram them all into a single script on the Player object.

The example here is just the client saying "server, I want to use this door" and letting the server do whatever is necessary for that. But without authority I can't interact with the object at all.

My guess is I'd have to call something on the player like:

 void Use(GameObject obj)
 {
     CmdUse(obj);
 }
 
 [Command]
 void CmdUse(GameObject obj)
 {
     obj.GetComponent<UsableObject>.Use();
 }

And make sure they have a method for every interaction type. I'm hoping I can do it from the object itself, but if not, would this be the least messy alternative?

Further explanation of how it is called:

Essentially it will create buttons for a tooltip with delgate commands to send when the buttons are clicked. The below is in the door script:

 public override void CreateUIInterface(Text titleText, GameObject buttonMenu, GameObject toolTipButton, PlayerInteraction playerInteraction)
 {
     titleText.text = GetName();
     GameObject obj = (GameObject)Instantiate(toolTipButton, buttonMenu.transform);
     obj.transform.SetParent(buttonMenu.transform);
     obj.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
     if (isOpen)
         obj.GetComponent<Button>().GetComponentInChildren<Text>().text = "Close";
     else
         obj.GetComponent<Button>().GetComponentInChildren<Text>().text = "Open";
     obj.GetComponent<Button>().onClick.AddListener(delegate { Use(); });
     obj.GetComponent<Button>().onClick.AddListener(delegate { playerInteraction.ClearTooltip(); });
     Debug.Log("Instantiated Open: " + toolTipButton.name + ", " + buttonMenu.transform.name);
     obj = (GameObject)Instantiate(toolTipButton, buttonMenu.transform);
     obj.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
     obj.GetComponent<Button>().GetComponentInChildren<Text>().text = "Destroy";
     obj.GetComponent<Button>().onClick.AddListener(delegate { Destroy(); });
     obj.GetComponent<Button>().onClick.AddListener(delegate { playerInteraction.ClearTooltip(); });
     obj = (GameObject)Instantiate(toolTipButton, buttonMenu.transform);
     obj.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
     obj.GetComponent<Button>().GetComponentInChildren<Text>().text = "Switch";
     obj.GetComponent<Button>().onClick.AddListener(delegate { Switch(); });
     obj.GetComponent<Button>().onClick.AddListener(delegate { playerInteraction.ClearTooltip(); });
 }

So with this it doesn't have an initial method that is called, it just goes straight to the door with the method. I have tried using the Command as the delegate but it's the same as calling it straight from the door itself (no authority).

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by Fikule · Jan 10, 2017 at 01:40 PM

I ended up going with something close to @fafase answer by re-writing a few bits. Essentially I now have:

PlayerInteractions.c

 //Interactions//

 public void Use(GameObject obj)
 {
     CmdUse(obj);
 }

 [Command]
 public void CmdUse(GameObject obj)
 {
     obj.GetComponent<UsableObject>().Use(1);
 }

PlayerInteractions will need a function and [Command] for each possible interaction within UseableObject. This script is located on the player, so this is the Use() (or other method) that will be called by the client.

ShipDoor.c

 public class ShipDoor : UsableObject
 {
     [SyncVar]
     bool isOpen = false;
 
     public override void CreateUIInterface(Text titleText, GameObject buttonMenu, GameObject toolTipButton, PlayerInteraction playerInteraction)
     {
         if (isOpen)
             buttonCommandList.Add(new UseableButton("Close", delegate { playerInteraction.Use(this.gameObject); }));
         else
             buttonCommandList.Add(new UseableButton("Open", delegate { playerInteraction.Use(this.gameObject); }));
         buttonCommandList.Add(new UseableButton("Destroy", this.Destroy));
         buttonCommandList.Add(new UseableButton("Switch", this.Switch));
 
         base.CreateUIInterface(titleText, buttonMenu, toolTipButton, playerInteraction);
 
         buttonCommandList.Clear();
     }
     
     public override void Use(int i)
     {
         isOpen = !isOpen;
         currentTransition = 0;
         if (isOpen)
             RemoveTag("Obstruction");
         else
             AddTag("Obstruction");
         Debug.Log("Opening " + m_name);
     }
 
     public override void Destroy()
     {
         Debug.Log("Destroying " + m_name);
     }
 
     public override void Switch()
     {
         Debug.Log("Switching " + m_name);
     }
 
 ...
 
 }

ShipDoor (inheriting from UseableObject) will have Use() called from PlayerInteractions by the server. This will let it be updated properly. CreateUIInterface() sets up a list of buttons for the object to display with the PlayerInteraction methods as it's calls, taking the object as a parameter.

UseableObject.c

 public class UsableObject : BaseObject
 {
     public string m_name;
 
     public class UseableButton
     {
         public UnityAction action;
         public string name;
 
         public UseableButton(string p_name, UnityAction p_action)
         {
             name = p_name;
             action = p_action;
         }
     }
     public List<UseableButton> buttonCommandList = new List<UseableButton>();
 
     public virtual void CreateUIInterface(Text titleText, GameObject buttonMenu, GameObject toolTipButton, PlayerInteraction playerInteraction)
     {
         Debug.Log("Creating Interface");
         titleText.text = GetName();
         foreach (UseableButton ubtn in buttonCommandList)
         {
             GameObject obj = (GameObject)Instantiate(toolTipButton, buttonMenu.transform);
             obj.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
             obj.GetComponent<Button>().GetComponentInChildren<Text>().text = ubtn.name;
             obj.GetComponent<Button>().onClick.AddListener(ubtn.action);
             obj.GetComponent<Button>().onClick.AddListener(delegate { playerInteraction.ClearTooltip(); });
         }
     }
 
     public virtual void Use(int i)
     {
         Debug.Log("Not Usable");
     }
 
     public virtual void Destroy()
     {
         Debug.Log("Not Usable");
     }
 
     public virtual void Switch()
     {
         Debug.Log("Not Usable");
     }
 
     public virtual string GetName()
     {
         return m_name;
     }
 }

UseableObject creates the buttons from the list and contains the virtual functions.

So now when a player clicks an object it creates a dialog with buttons having methods for PlayerInteraction calls with the object in question. Clicking the button will then have the server call back to the object originally clicked. This lets me keep the functionality on the object itself.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by fafase · Jan 08, 2017 at 10:18 AM

Try to get the Command method on the calling object. In your example you have the public Use method on Object A which I assume is called from another object B. That Use method calls the Command method on A.

Try with B object script as such:

 void Method(){
      if(condition){ CmdCall()}
 } 
 [Command]
 private void CmdCall(){
      FindObjectOfType<ShipDoor>().Use();
 }

And this is ship door:

public class ShipDoor : UsableObject // UsuableObject inherits from NetworkBehavior { [SyncVar] bool isOpen = false; [SyncVar] float currentTransition = 0;

  public override void Use()
  {
      UseDoor();
      Debug.Log("Opening " + m_name);
  }
 
  void UseDoor()
  {
      Debug.Log("Opening(Server) " + m_name);
      isOpen = !isOpen;
      currentTransition = 0;
  }

}

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Fikule · Jan 08, 2017 at 03:25 PM 0
Share

Unfortunately this won't work due to how the call to Use() is set up, sorry for omitting that. I've updated my original post with the setup for the call.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Unity networking tutorial? 6 Answers

Can a person be both a client and a server? (and more) 0 Answers

UNET : SyncVar value not updating, hooks firing 1 Answer

Commands being called with the host, but not a client. 1 Answer

SpawnWithClientAuthority issues 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges