Unity 接入微信小游戏后常用的功能

1. 前言

近期项目需要将游戏投放到国内各个小游戏平台,微信小游戏,抖音小游戏,快手小游戏,支付宝小游戏等,各种小游戏平台,之前有处理过国内安卓 谷歌 IOS 平台的打包,后边会一一说明各小游戏平台用到的不同的功能。
关于游戏上架微信的博客有很多,此篇主要提一下我上到微信后用到的微信方面相关的功能,分享,邀请,订阅,朋友圈,振动,好友排行等,后台等相关应用,具体还要根据大家项目自行修改。

2.微信相关文档

普通的Unity版本可以参考下方链接接入

3.关于打包微信的应用

1.处理掉烦人的LOGO

  • 代码处理
[Preserve]
public class SkipUnityLogo
{
    
    
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    private static void BeforeSplashScreen()
    {
    
    
#if UNITY_WEBGL
        Application.focusChanged += Application_focusChanged;
#else
        System.Threading.Tasks.Task.Run(AsyncSkip);
#endif
    }
 
#if UNITY_WEBGL
    private static void Application_focusChanged(bool obj)
    {
    
    
        Application.focusChanged -= Application_focusChanged;
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }
#else
    private static void AsyncSkip()
    {
    
    
        SplashScreen.Stop(SplashScreen.StopBehavior.StopImmediate);
    }
#endif
}
#endif

2.微信分享或邀请

微信当中可以自定义参数,来获取来进行分享或邀请

			GetShareInfo(type, WeChatConfig.ShareLocationInvite, (info) =>
			{
    
    
				LY.Analytics.ShareEvent(WeChatConfig.ShareLocationMenu, type, DataAnalytics.ShareAction.show, info.combineShareId);
				string str = $"sharelocation={
      
      info.location}&combineShareId={
      
      info.combineShareId}&action=normal"; //分享
				string str = $"sharelocation={
      
      info.location}&combineShareId={
      
      info.combineShareId}&inviter={
      
      LY.SDK.GetOpenId()}&action=invite"; //邀请
				_inviteCallback = cb;
				_shareStartTime = DateTime.Now;
				ShareAppMessageOption shareAppMessageOption = new ShareAppMessageOption();
				shareAppMessageOption.title = info.text;
				shareAppMessageOption.imageUrl = info.imageUrl;  //自己CDN配置的图片地址
				shareAppMessageOption.query = str;
				WX.ShareAppMessage(shareAppMessageOption); //调用
			}, (error) =>
			{
    
    
				cb?.Invoke(false);
			});

微信也会返回受邀新用户数量,可以用来做邀请有礼等游戏内容

3.游戏圈

游戏圈的难点,基本就在入口的位置计算上,另外就是我们UI还是用的美术出的效果图,但是向微信上传的是一张空白图

  • 第一步计算生成位置
 public static void CreateClubBtn(Transform tra, Camera camera = null)
		{
    
    
#if  !UNITY_EDITOR
			if (camera == null) 
			{
    
     
				camera = Camera.main;
			}

			WeChatWASM.SystemInfo systemInfo = WX.GetSystemInfoSync(); ;
			float ux = Screen.width;
			float uy = Screen.height;
			float wx = (float)systemInfo.screenWidth;
			float wy = (float)systemInfo.screenHeight;
			float sx = wx / ux;
			float sy = wy / uy;

			Vector3 screenPoint = RectTransformUtility.WorldToScreenPoint(camera, tra.position);
			Rect r = Utility.UGUI.GetSize(tra.gameObject);
			float bw = r.width * sx;
			float bh = r.height * sy;
			float bx = screenPoint.x * sx - bw * 0.5f;
			float by = (Screen.height - screenPoint.y) * sy - bh * 0.5f;

			Debug.Log($"CreateClubBtn++++ux:{
      
      ux} uy:{
      
      uy} wx:{
      
      wx} wy:{
      
      wy} sx:{
      
      sx} sy:{
      
      sy} bw:{
      
      bw} bh:{
      
      bh} bx:{
      
      bx} by:{
      
      by} screenX:{
      
      screenPoint.x} screenY:{
      
      screenPoint.y}");
			CreateClubBtn(new Rect(bx,by,bw,bh));

		
#endif
		}
  • 生成出对应功能
public static void CreateClubBtn(Rect rect)
		{
    
    
#if  !UNITY_EDITOR
			if (clubBtn != null)
			{
    
    
				return;
			}
			clubBtn = WX.CreateGameClubButton(new WXCreateGameClubButtonParam()
			{
    
    
				type = GameClubButtonType.image,
				text = "游戏圈",
				image = WeChatConfig.ClubImageUrl,
				style = new GameClubButtonStyle()
				{
    
    
					left = (int)rect.x,
					top = (int)rect.y,
					width = (int)rect.width,
					height = (int)rect.height,
				}
			});
			clubBtn.Show();
#endif
		}
  • 控制朋友圈按钮显示,隐藏,销毁
 public static void ShowClubkBtn()
		{
    
    
#if LIEYOU_WECHAT
			clubBtn?.Show();
#endif
		}

		public static void HideClubkBtn()
		{
    
    
#if LIEYOU_WECHAT
			clubBtn?.Hide();
#endif
		}

		public static void DestroyClubkBtn()
		{
    
    
#if LIEYOU_WECHAT
			clubBtn?.Destroy();
			clubBtn = null;
#endif
		}
  • 可以将奖励发放,放在 OnTap 中
 public static void OnTapAc(Action action)
		{
    
    
#if LIEYOU_WECHAT && !UNITY_EDITOR
clubBtn.OnTap(() => {
    
    
                if (action != null)
                {
    
    
					action();
				}
			});
#endif
		}

4.微信振动

根据需求调用长振动还是短振动就可以

public override void VibrateShort()
		{
    
    
			base.VibrateShort();
			VibrateShortOption result = new VibrateShortOption();
			WX.VibrateShort(result);
		}

		public override void VibrateLong()
		{
    
    
			base.VibrateLong();
			VibrateShortOption result = new VibrateShortOption();
			result.type = "heavy";
			WX.VibrateShort(result);
		}

5.微信个人信息获取

你可以通过 微信返回的配置信息 WeChatWASM.GetSettingOption,来判断有没有授权个人信息,当然也有简单的办法,我在下边会先进行个人信息获取,如果没有获取到,创建弹窗授权获取。

public override void GetUserInfo(Action<DataStruct.UserInfo> suc, Action<int, string> fail = null)
	{
    
    
		base.GetUserInfo(suc, fail);

		if (_userInfo != null)
		{
    
    
			suc?.Invoke(_userInfo);
			return;
		}

		GetUserInfoOption getUserInfoOption = new GetUserInfoOption();
		getUserInfoOption.success += (GetUserInfoSuccessCallbackResult res) =>
		{
    
    
			_userInfo = new DataStruct.UserInfo();
			_userInfo.nickName = res.userInfo.nickName;
			_userInfo.avatarUrl = res.userInfo.avatarUrl;
			suc?.Invoke(_userInfo);
		};
		getUserInfoOption.fail += (GeneralCallbackResult res) =>
		{
    
    
			WXUserInfoButton btn = WX.CreateUserInfoButton(0, 0, 5000, 5000, "en", false);
			btn.OnTap((WXUserInfoResponse res) =>
			{
    
    
				if (res.errCode == 0)
				{
    
    
					_userInfo = new DataStruct.UserInfo();
					_userInfo.nickName = res.userInfo.nickName;
					_userInfo.avatarUrl = res.userInfo.avatarUrl;
					suc?.Invoke(_userInfo);
				}
				else
				{
    
    
					fail?.Invoke(res.errCode, res.errMsg);
				}
				btn.Destroy();
			});
		};
		WX.GetUserInfo(getUserInfoOption);
	}

6.微信订阅功能

订阅可能需要处理的比较麻烦些,由于Unity WebGL发布的多点触控存在问题, 导致在微信中多点触控存在粘连的情况,所以需要使用WX的触控接口重新覆盖Unity的BaseInput关于触控方面的接口,通过设置StandaloneInputModule.inputOverride的方式来实现,这块儿比较多,感觉能单独写一篇,下边简单提要一些。

可以写一个 WXTouchInputOverride : BaseInput 脚本进行管理

private void OnWxTouchStart(OnTouchStartListenerResult touchEvent)
    {
    
    
        foreach (var wxTouch in touchEvent.changedTouches)
        {
    
    
            var data = FindOrCreateTouchData(wxTouch.identifier);
            data.touch.phase = TouchPhase.Began;
            data.touch.position = new Vector2(wxTouch.clientX, wxTouch.clientY);
            data.touch.rawPosition = data.touch.position;
            data.timeStamp = touchEvent.timeStamp;

            // Debug.Log($"OnWxTouchStart:{wxTouch.identifier}, {data.touch.phase}");
        }
    }

    private void OnWxTouchMove(OnTouchStartListenerResult touchEvent)
    {
    
    
        foreach (var wxTouch in touchEvent.changedTouches)
        {
    
    
            var data = FindOrCreateTouchData(wxTouch.identifier);
            UpdateTouchData(data, new Vector2(wxTouch.clientX, wxTouch.clientY), touchEvent.timeStamp, TouchPhase.Moved);
        }
    }

    private void OnWxTouchEnd(OnTouchStartListenerResult touchEvent)
    {
    
    
        foreach (var wxTouch in touchEvent.changedTouches)
        {
    
    
            TouchData data = FindTouchData(wxTouch.identifier);
            if (data == null)
            {
    
    
                Debug.LogError($"OnWxTouchEnd, error identifier:{
      
      wxTouch.identifier}");
                return;
            }

            if (data.touch.phase == TouchPhase.Canceled || data.touch.phase == TouchPhase.Ended)
            {
    
    
                Debug.LogWarning($"OnWxTouchEnd, error phase:{
      
      wxTouch.identifier}, phase:{
      
      data.touch.phase}");
            }

            // Debug.Log($"OnWxTouchEnd:{wxTouch.identifier}");
            UpdateTouchData(data, new Vector2(wxTouch.clientX, wxTouch.clientY), touchEvent.timeStamp, TouchPhase.Ended);
        }

        GameObject selectedObject = EventSystem.current.currentSelectedGameObject;
        if (selectedObject != null)
        {
    
    
            Button button = selectedObject.GetComponent<Button>();
            if (button != null)
            {
    
    
                int clickListenerCount = button.onClick.GetPersistentEventCount();
                if (clickListenerCount > 0) {
    
    
                    button.onClick.SetPersistentListenerState(0, UnityEventCallState.EditorAndRuntime);
                    button.onClick.Invoke();
                    button.onClick.SetPersistentListenerState(0, UnityEventCallState.Off);
                }
            }
        }
    }

    private void OnWxTouchCancel(OnTouchStartListenerResult touchEvent)
    {
    
    
        foreach (var wxTouch in touchEvent.changedTouches)
        {
    
    
            TouchData data = FindTouchData(wxTouch.identifier);
            if (data == null)
            {
    
    
                Debug.LogError($"OnWxTouchCancel, error identifier:{
      
      wxTouch.identifier}");
                return;
            }

            if (data.touch.phase == TouchPhase.Canceled || data.touch.phase == TouchPhase.Ended)
            {
    
    
                Debug.LogWarning($"OnWxTouchCancel, error phase:{
      
      wxTouch.identifier}, phase:{
      
      data.touch.phase}");
            }

            // Debug.Log($"OnWxTouchCancel:{wxTouch.identifier}");
            UpdateTouchData(data, new Vector2(wxTouch.clientX, wxTouch.clientY), touchEvent.timeStamp, TouchPhase.Canceled);
        }
    }

进行方法绑定

private void RegisterWechatTouchEvents()
    {
    
    
        WX.OnTouchStart(OnWxTouchStart);
        WX.OnTouchMove(OnWxTouchMove);
        WX.OnTouchEnd(OnWxTouchEnd);
        WX.OnTouchCancel(OnWxTouchCancel);
    }

    private void UnregisterWechatTouchEvents()
    {
    
    
        WX.OffTouchStart(OnWxTouchStart);
        WX.OffTouchMove(OnWxTouchMove);
        WX.OffTouchEnd(OnWxTouchEnd);
        WX.OffTouchCancel(OnWxTouchCancel);
    }

然后,判定是否有订阅权限

public void HasPermission(string templateId, Action suc, Action<string> fail)
		{
    
    
#if UNITY_EDITOR
			fail?.Invoke("not support");
#elif LIEYOU_WECHAT
			WeChatWASM.GetSettingOption option = new WeChatWASM.GetSettingOption();
			option.withSubscriptions = true;
			option.success += (WeChatWASM.GetSettingSuccessCallbackResult r) =>
			{
    
    
				WeChatWASM.SubscriptionsSetting set = r.subscriptionsSetting;
				Dictionary<string, string> dict = set.itemSettings;
				if (r.subscriptionsSetting != null && r.subscriptionsSetting.mainSwitch && dict.ContainsKey(templateId) && dict[templateId].Equals("accept"))
				{
    
    
					suc?.Invoke();
				}
				else
				{
    
    
					fail?.Invoke("No permission");
				}
			};
			option.fail += (error) =>
			{
    
    
				fail.Invoke(error.errMsg);
			};
			WeChatWASM.WX.GetSetting(option);
#endif
		}

紧接着, 请求版本更新权限

		public void RequestVersionUpdate(Action suc, Action<string> fail)
		{
    
    
#if UNITY_EDITOR
			fail?.Invoke("not support");
#elif LIEYOU_WECHAT
			WeChatWASM.RequestSubscribeSystemMessageOption request = new WeChatWASM.RequestSubscribeSystemMessageOption();
			string[] list = {
    
     "SYS_MSG_TYPE_WHATS_NEW" };
			request.msgTypeList = list;
			request.success += (rr) =>
			{
    
    
				Utility.Debug.LogSDK("SubscribeManager.RequestVersionUpdate.suc");
				suc?.Invoke();
			};
			request.fail += (rr) =>
			{
    
    
				fail?.Invoke(rr.errMsg);
			};
			WeChatWASM.WX.RequestSubscribeSystemMessage(request);
#endif
		}

顺带提一下 申请订阅权限,一次不能超过3个

	public void RequestSubscribeMessage(string[] tmplIds, Action suc, Action<string> fail)
		{
    
    
			Utility.Debug.LogSDK("SubscribeManager.RequestSubscribeMessage");
#if UNITY_EDITOR
			fail?.Invoke("not support");
#elif LIEYOU_WECHAT
			WeChatWASM.RequestSubscribeMessageOption request = new WeChatWASM.RequestSubscribeMessageOption();
			request.tmplIds = tmplIds;
			request.fail += (r) =>
			{
    
    
				Utility.Debug.LogSDK("RequestSubscribeMessage.fail = " + r.errMsg);
				fail?.Invoke(r.errMsg);
			};
			request.success += (r) =>
			{
    
    
				Utility.Debug.LogSDK("RequestSubscribeMessage.suc");
				suc?.Invoke();
			};
			WeChatWASM.WX.RequestSubscribeMessage(request);
#endif
		}

7.微信好友排行榜

微信好友排行默认使用 Layout布局,且需要用到 JS引擎,由于项目组人少且并无前端,周期不允许,用自带open-data无法满足展示多样性界面的需求,但也是有别的办法,这里只能粗略说一下,具体实现布局啥的没办法详细展开,内容也比较多。
目前可使用TS来实现布局,界面的搭建以及与主域的交互跟微信的交互

布局相关
在这里插入图片描述
基础组件相关
在这里插入图片描述
npm run build 来生成界面出来直接替换掉wx自带的开放域
在这里插入图片描述

需要提一下,字体是需要主域共享给开放域的,无法在开放域中添加

8.微信广告

微信广告用到的基本上包括 激励,插屏,横幅,简单写一下实现

  • 激励广告
void LoadReward()
		{
    
    
			if (_rewardAd == null)
			{
    
    
				WXCreateRewardedVideoAdParam param = new WXCreateRewardedVideoAdParam();
				param.adUnitId = WeChatConfig.RewardId;
				_rewardAd = WX.CreateRewardedVideoAd(param);
				_rewardAd.OnError((r) =>
				{
    
    
					Utility.Debug.LogSDK("WXReward.OnError = " + r.errMsg);
					IsRewardSuc = false;
					RewardAdFail?.Invoke();
				});
				_rewardAd.OnLoad((r) =>
				{
    
    
					_isLoadedReward = true;
					//其他
				});
				_rewardAd.OnClose((r) =>
				{
    
    					
					if (r != null && r.isEnded)
					{
    
    
						IsRewardSuc = true;
						RewardAdSuc?.Invoke();
						
						if (_videoIsInters)
						{
    
    
							SDKEvent.SpotVideoFinishCall?.Invoke();
						}
					}
					else
					{
    
    
						IsRewardSuc = false;
						RewardAdFail?.Invoke();
					}
					SDKEvent.RewardClose?.Invoke(IsRewardSuc);
					_videoIsInters = false;
				});
				_rewardAd.Load();
			}
		}
  • 插屏广告
void LoadInters()
		{
    
    
			if (_interstitialAd == null)
			{
    
    
				WXCreateInterstitialAdParam param = new WXCreateInterstitialAdParam();
				param.adUnitId = WeChatConfig.IntersId;
				_interstitialAd = WX.CreateInterstitialAd(param);
				_interstitialAd.OnLoad((r) =>
				{
    
    
					_isLoadedInters = true;
					//数据打点
				});
				_interstitialAd.OnClose(() =>
				{
    
    
					InterstitialCallback?.Invoke(true);
					SDKEvent.IntersClose?.Invoke();
					_interstitialAd.Load();
					//数据打点
				});
				_interstitialAd.OnError((r) =>
				{
    
    
					InterstitialCallback?.Invoke(false);
					SDKEvent.IntersClose?.Invoke();
					Utility.Debug.LogSDK(r.errMsg);
				});
				_interstitialAd.Load();
			}
		}
  • Banner广告
void LoadBanner(Rect rect)
		{
    
    
			if (rect == default)
			{
    
    
				rect = WeChatConfig.BannerStyle;
			}
			//数据打点
			WXCreateBannerAdParam param = new WXCreateBannerAdParam();
			param.adUnitId = WeChatConfig.BannerId;
			Style style = new Style();
			style.height = (int)rect.height;
			style.width = (int)rect.width;
			style.top = (int)rect.y;
			style.left = (int)rect.x;
			param.style = style;

			//_bannerAd = WX.CreateBannerAd(param);
			_bannerAd = WX.CreateFixedBottomMiddleBannerAd(param.adUnitId, 30, style.height);
			_bannerAd.OnLoad((r) =>
			{
    
    
				Utility.Debug.LogSDK("MPWeChat.LoadBanner.OnLoad top = " + style.top + " left = " + style.left + " height = " + style.height + " width = " + style.width);
			});
			_bannerAd.OnError((r) =>
			{
    
    
				Utility.Debug.LogSDK(r.errMsg);
			});
		}

8.微信充值

微信充值这里要分一下,ios跟安卓,双端充值流程与补单流程并不一样,只简单粗略提一下

		/// ios 充值
		private void CallPayIOS(DataStruct.GoodsData d, Action<DataStruct.GoodsData> suc, Action<string> fail)
		{
    
    
			//Debug.Log("   支付测试  开始启用ios弹窗  ISO_openCustomerService");
			OpenCustomerServiceConversationOption obj = new OpenCustomerServiceConversationOption();

			//会话来源
			obj.sessionFrom = "{'Title': '游戏充值','productId':" + "'" + d.orderId + "'" + "}";

			//是否显示会话内消息卡片,
			//设置此参数为 true,
			//用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
			//用户点击后可以快速发送小程序消息
			obj.showMessageCard = true;
			obj.sendMessageTitle = "游戏充值";

			//会话内消息卡片路径
			obj.sendMessagePath = d.orderId;

			//会话内消息卡片图片路径
			obj.sendMessageImg = WeChatConfig.IosPayMessageImg;
			obj.success = (response) =>
			{
    
    
				IOSPayQuery(d, suc, fail);
			};
			obj.fail = (err) =>
			{
    
    
				Utility.Debug.LogSDK("LYWeChat.CallPayIOS.fail:" + err.errMsg);
				fail?.Invoke(err.errMsg);
			};
			WX.OpenCustomerServiceConversation (obj);
		}

如果没有道具到账,但是进行了扣款,就需要主动进行补单
安卓,需要借助米大师来实现充值补单的流程, 需要先进行米大师余额的查询与扣除

		/// <summary>
		/// 米大师支付
		/// </summary>
		/// <param name="d"></param>
		/// <param name="suc"></param>
		/// <param name="fail"></param>
		private void MidasPayment(DataStruct.GoodsData d, Action<DataStruct.GoodsData> suc, Action<string> fail)
		{
    
    
			RequestMidasPaymentOption callBack = new RequestMidasPaymentOption();
			callBack.mode = "game";//支付的类型,不同的支付类型有各自额外要传的附加参数。
			callBack.env = 0; //环境配置
			callBack.offerId = WeChatConfig.OfferID;//在米大师侧申请的应用 id
			callBack.currencyType = "CNY";//币种
			callBack.platform = "android";//申请接入时的平台,platform 与应用 id 有关。
			callBack.buyQuantity = (int)(d.price * WeChatConfig.BuyQuantityScale);//购买数量。mode=game 时必填。购买数量。详见 buyQuantity 限制说明。
			callBack.zoneId = "1";//分区 ID
			callBack.success = (callResult) =>
			{
    
    
				SetOrderStatus(d.orderId, LY_OrderStatus.buy, () =>
				{
    
    
					suc?.Invoke(d);
					SDKEvent.PaySuc?.Invoke(d);
				}, fail);
			};
			callBack.fail = (err) =>
			{
    
    
				fail?.Invoke(err.errMsg);
			};
			WX.RequestMidasPayment(callBack);
		}

4.总结

此篇讲了一些接入微信后可能用到的功能,当然还有些基础功能,WxHide,WxShow,微信锁帧,后台,常亮等等,比较简单,就不做整理了,希望对大家能有一些帮助。

猜你喜欢

转载自blog.csdn.net/sNameNo/article/details/142483603