任务32:前端-游戏交互操作控制,抢地主出牌等

前端部分

本节增加了交互操作需要的界面资源,下载后放到\Assets\Bundles\Landlords

LandInteraction.zip (2.80KB)

PromptButton是获得出牌提示按钮

PlayButton与DiscardButton是出牌与放弃出牌按钮

GrabButton与DisgrabButton是抢地主与放弃抢地主按钮

增加游戏操控交互组件

没什么难理解的,交互操控的服务端请求都在LandInteractionComponent组件中。

LandInteractionComponent

\Assets\Model\Landlords\LandUI\LandInteraction\LandInteractionComponent.cs

using UnityEngine;
using UnityEngine.UI;
using ETModel;
using System.Collections.Generic;

namespace ETModel
{
    [ObjectSystem]
    public class LandInteractionComponentAwakeSystem : AwakeSystem<LandInteractionComponent>
    {
        public override void Awake(LandInteractionComponent self)
        {
            self.Awake();
        }
    }

    public class LandInteractionComponent : Component
    {
        private Button playButton;  //出牌
        private Button promptButton;  //提示
        private Button discardButton;  //不出
        private Button grabButton;   //抢地主
        private Button disgrabButton;  //不抢

        private List<Card> currentSelectCards = new List<Card>();

        public bool IsFirst { get; set; }

        public void Awake()
        {
            ReferenceCollector rc = this.GetParent<UI>().GameObject.GetComponent<ReferenceCollector>();

            playButton = rc.Get<GameObject>("PlayButton").GetComponent<Button>();
            promptButton = rc.Get<GameObject>("PromptButton").GetComponent<Button>();
            discardButton = rc.Get<GameObject>("DiscardButton").GetComponent<Button>();
            grabButton = rc.Get<GameObject>("GrabButton").GetComponent<Button>();
            disgrabButton = rc.Get<GameObject>("DisgrabButton").GetComponent<Button>();
        

            //绑定事件
            playButton.onClick.Add(OnPlay);
            promptButton.onClick.Add(OnPrompt);
            discardButton.onClick.Add(OnDiscard);
            grabButton.onClick.Add(OnGrab);
            disgrabButton.onClick.Add(OnDisgrab);

            //默认隐藏UI
            playButton.gameObject.SetActive(false);
            promptButton.gameObject.SetActive(false);
            discardButton.gameObject.SetActive(false);
            grabButton.gameObject.SetActive(false);
            disgrabButton.gameObject.SetActive(false);
        }

        public override void Dispose()
        {
            if(this.IsDisposed)
            {
                return;
            }

            base.Dispose();

            Game.Scene.GetComponent<ResourcesComponent>()?.UnloadBundle($"{LandUIType.LandInteraction}.unity3d");
        }


        /// <summary>
        /// 选中卡牌
        /// </summary>
        /// <param name="card"></param>
        public void SelectCard(Card card)
        {
            currentSelectCards.Add(card);
        }

        /// <summary>
        /// 取消选中卡牌
        /// </summary>
        /// <param name="card"></param>
        public void CancelCard(Card card)
        {
            currentSelectCards.Remove(card);
        }

        /// <summary>
        /// 清空选中卡牌
        /// </summary>
        public void Clear()
        {
            currentSelectCards.Clear();
        }

        /// <summary>
        /// 开始抢地主
        /// </summary>
        public void StartGrab()
        {
            grabButton.gameObject.SetActive(true);
            disgrabButton.gameObject.SetActive(true);
        }

        /// <summary>
        /// 开始出牌
        /// </summary>
        public void StartPlay()
        {
            playButton.gameObject.SetActive(true);
            promptButton.gameObject.SetActive(!IsFirst);
            discardButton.gameObject.SetActive(!IsFirst);
        }

        /// <summary>
        /// 结束抢地主
        /// </summary>
        public void EndGrab()
        {
            grabButton.gameObject.SetActive(false);
            disgrabButton.gameObject.SetActive(false);
        }

        /// <summary>
        /// 结束出牌
        /// </summary>
        public void EndPlay()
        {
            playButton.gameObject.SetActive(false);
            promptButton.gameObject.SetActive(false);
            discardButton.gameObject.SetActive(false);
        }


        /// <summary>
        /// 出牌
        /// </summary>
        private async void OnPlay()
        {
            CardHelper.Sort(currentSelectCards);
            Actor_GamerPlayCard_Req request = new Actor_GamerPlayCard_Req();
            foreach(var a in currentSelectCards)
            {
                request.Cards.Add(a);
            }
            Actor_GamerPlayCard_Back response = (Actor_GamerPlayCard_Back)await SessionComponent.Instance.Session.Call(request);

            //出牌错误提示
            LandlordsGamerPanelComponent gamerUI = LandRoomComponent.LocalGamer.GetComponent<LandlordsGamerPanelComponent>();
            if (response.Error == ErrorCode.ERR_PlayCardError)
            {
                gamerUI.SetPlayCardsError();
            }
        }

        /// <summary>
        /// 提示
        /// </summary>
        private async void OnPrompt()
        {
            Actor_GamerPrompt_Req request = new Actor_GamerPrompt_Req();
            Actor_GamerPrompt_Back response = (Actor_GamerPrompt_Back)await SessionComponent.Instance.Session.Call(request);
            
            HandCardsComponent handCards = LandRoomComponent.LocalGamer.GetComponent<HandCardsComponent>();

            //清空当前选中
            while (currentSelectCards.Count > 0)
            {
                Card selectCard = currentSelectCards[currentSelectCards.Count - 1];
                handCards.GetSprite(selectCard).GetComponent<HandCardSprite>().OnClick(null);
            }

            //自动选中提示出牌
            if (response.Cards != null)
            {
                for (int i = 0; i < response.Cards.Count; i++)
                {
                    handCards.GetSprite(response.Cards[i]).GetComponent<HandCardSprite>().OnClick(null);
                }
            }
        }

        /// <summary>
        /// 不出
        /// </summary>
        private void OnDiscard()
        {
            SessionComponent.Instance.Session.Send(new Actor_GamerDontPlayCard_Ntt());
        }

        /// <summary>
        /// 抢地主
        /// </summary>
        private void OnGrab()
        {
            SessionComponent.Instance.Session.Send(new Actor_GamerGrabLandlordSelect_Ntt() { IsGrab = true });
        }

        /// <summary>
        /// 不抢
        /// </summary>
        private void OnDisgrab()
        {
            SessionComponent.Instance.Session.Send(new Actor_GamerGrabLandlordSelect_Ntt() { IsGrab = false });
        }
    }
}

LandInteractionFactory

\Assets\Model\Landlords\LandUI\LandInteraction\LandInteractionFactory.cs

using ETModel;
using System;
using UnityEngine;

namespace ETModel
{
    public class LandInteractionFactory
    {
        public static UI Create(string type, UI parent)
        {
            ResourcesComponent resourcesComponent = ETModel.Game.Scene.GetComponent<ResourcesComponent>();
            resourcesComponent.LoadBundle($"{type}.unity3d");
            GameObject prefab = (GameObject)resourcesComponent.GetAsset($"{type}.unity3d", $"{type}");
            GameObject interaction = UnityEngine.Object.Instantiate(prefab);

            interaction.layer = LayerMask.NameToLayer("UI");

            UI ui = ComponentFactory.Create<UI, GameObject>(interaction);
            parent.Add(ui);
            ui.GameObject.transform.SetParent(parent.GameObject.transform, false);

            ui.AddComponent<LandInteractionComponent>();
            return ui;
        }
    }
}

LandRoomComponent组件中添加Interaction

\Assets\Model\Landlords\LandUI\LandRoom\LandRoomComponent.cs

        public LandInteractionComponent interaction;

        public LandInteractionComponent Interaction
        {
            get
            {
                if (interaction == null)
                {
                    UI uiRoom = this.GetParent<UI>();
                    UI uiInteraction = LandInteractionFactory.Create(LandUIType.LandInteraction, uiRoom);
                    interaction = uiInteraction.GetComponent<LandInteractionComponent>();
                }
                return interaction;
            }
        }

添加 HandCardSprite 使手牌可以交互

这个脚本挂在手牌元件上,这样所有手牌就可以点选和取消选择了

\Assets\Model\Landlords\Other\HandCardSprite.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace ETModel
{
    public class HandCardSprite : MonoBehaviour
    {
        public Card Poker { get; set; }
        private bool isSelect;

        void Start()
        {
            EventTrigger eventTrigger = gameObject.AddComponent<EventTrigger>();
            eventTrigger.triggers = new List<EventTrigger.Entry>();
            EventTrigger.Entry clickEntry = new EventTrigger.Entry();
            clickEntry.eventID = EventTriggerType.PointerClick;
            clickEntry.callback = new EventTrigger.TriggerEvent();
            clickEntry.callback.AddListener(new UnityAction<BaseEventData>(OnClick));
            eventTrigger.triggers.Add(clickEntry);
        }

        public void OnClick(BaseEventData data)
        {
            float move = 50.0f;
            if (isSelect)
            {
                move = -move;
                Game.EventSystem.Run(EventIdType.CancelHandCard, Poker);
            }
            else
            {
                Game.EventSystem.Run(EventIdType.SelectHandCard, Poker);
            }
            RectTransform rectTransform = this.GetComponent<RectTransform>();
            rectTransform.anchoredPosition += Vector2.up * move;
            isSelect = !isSelect;
        }
    }
}

添加选择与取消选择手牌的事件

SelectHandCardEvent

\Assets\Model\Landlords\LandUI\Event\SelectHandCardEvent.cs

namespace ETModel
{
    [Event(ETModel.EventIdType.SelectHandCard)]
    public class SelectHandCardEvent : AEvent<Card>
    {
        public override void Run(Card card)
        {
            Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom).GetComponent<LandRoomComponent>().Interaction.SelectCard(card);
        }
    }
}

CancelHandCardEvent

\Assets\Model\Landlords\LandUI\Event\CancelHandCardEvent.cs

namespace ETModel
{
    [Event(ETModel.EventIdType.CancelHandCard)]
    public class CancelHandCardEvent : AEvent<Card>
    {
        public override void Run(Card card)
        {
            Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom).GetComponent<LandRoomComponent>().Interaction.CancelCard(card);
        }
    }
}

前端收到广播显示抢地主交互Actor_AuthorityGrabLandlord_NttHandler

\Assets\Model\Landlords\Handler\Actor05_Grab_LandlordAuthority_NttHandler.cs

using System;
using System.Collections.Generic;

namespace ETModel
{
    [MessageHandler]
    public class Actor_AuthorityGrabLandlord_NttHandler : AMHandler<Actor_AuthorityGrabLandlord_Ntt>
    {
        protected override async ETTask Run(ETModel.Session session, Actor_AuthorityGrabLandlord_Ntt message)
        {
            UI uiRoom = Game.Scene.GetComponent<UIComponent>().Get(LandUIType.LandRoom);

            if (message.UserID == LandRoomComponent.LocalGamer.UserID)
            {
                //显示抢地主交互
                uiRoom.GetComponent<LandRoomComponent>().Interaction.StartGrab();
            }

            await ETTask.CompletedTask;
        }
    }
}

LandlordsGamerPanelComponent添加出牌错误提示

\Assets\Model\Landlords\Component\LandlordsGamerPanelComponent.cs

        /// <summary>
        /// 出牌错误
        /// </summary>
        public void SetPlayCardsError()
        {
            prompt.text = "您出的牌不符合规则!";
        }

LandUIType添加LandInteraction

\Assets\Model\Landlords\LandUI\UIEventType.cs

public const string LandInteraction = "LandInteraction";

交互操控请求消息定义

可以看到下面消息体中用到repeated Card Cards = 1,Card在OuterMessage中定义的,所以本节这些方法我们也在OuterMessage中定义。(本课程HotfixMessage与OuterMessage并没有区别,你全部放HotfixMessage中也可以的,但是要注意一点,如果消息字段中有repeated Card Cards这类,那这个消息就要与Card 类定义在同一个消息定义文件,并且在这个类定义之后)

\Proto\OuterMessage.proto

//游戏交互操控消息=====>
message Actor_GamerPlayCard_Req // IActorRequest
{
    int32 RpcId = 90;
    int64 ActorId = 93;
    repeated Card Cards = 1;
    
}

message Actor_GamerPlayCard_Back // IActorResponse
{
    int32 RpcId = 90;
    int32 Error = 91;
    string Message = 92;
}

message Actor_GamerDontPlayCard_Ntt // IActorMessage
{
    int32 RpcId = 90;
    int64 ActorId = 93;
    int64 UserID = 1;
}

message Actor_GamerPrompt_Req // IActorRequest
{
    int32 RpcId = 90;
    int64 ActorId = 93;
}

message Actor_GamerPrompt_Back // IActorResponse
{
    int32 RpcId = 90;
    int32 Error = 91;
    string Message = 92;
    repeated Card Cards = 1;
}
//开始抢地主消息
message Actor_AuthorityGrabLandlord_Ntt // IActorMessage
{
    int32 RpcId = 90;
    int64 ActorId = 93;
    int64 UserID = 1;
}
//选择抢地方消息
message Actor_GamerGrabLandlordSelect_Ntt // IActorMessage
{
    int32 RpcId = 90;
    int64 ActorId = 93;
    int64 UserID = 1;
    bool IsGrab = 2;
}
//设置地主消息
message Actor_SetLandlord_Ntt // IActorMessage
{
    int32 RpcId = 90;
    int64 ActorId = 93;
    int64 UserID = 1;
    repeated Card LordCards = 2;
}

生成消息文件后,运行前后端项目(需要打包输出在Release中运行三个游戏程序分别登录账号,全部准备游戏后,才会显示游戏交互界面)。

后端部分

为了在前端显示操控交互界面需要在服务端增加随机先手的方法

添加随机先手方法的调用

GameControllerComponentSystem组件扩展类中有StartGame方法,

\Server\Hotfix\Landlords\System\GameControllerComponentSystem.cs

//随机先手玩家
gameController.RandomFirstAuthority();

还有随机先手玩家的方法

        /// <summary>
        /// 随机先手玩家
        /// </summary>
        public static void RandomFirstAuthority(this GameControllerComponent self)
        {
            Room room = self.GetParent<Room>();
            Gamer[] gamers = room.gamers;

            int index = RandomHelper.RandomNumber(0, gamers.Length);
            long firstAuthority = gamers[index].UserID;

            //广播先手抢地主玩家
            room.Broadcast(new Actor_AuthorityGrabLandlord_Ntt() { UserID = firstAuthority });
        } 

操控交互界面显示效果

此时服务端还没有交互操作请求对应的功能,所以只能看到交互操作界面,还不能抢地主,也没有出牌按钮。

后面的课时我们来实现这些交互操作对应的服务端功能。

 

posted @ 2023-02-16 14:35  Domefy  阅读(32)  评论(0编辑  收藏  举报