草庐IT

cocos creator上架字节跳动(抖音)小游戏注意事项(匿名登录、录屏、分享等踩坑记录)

灰机大大 2024-04-28 原文

常见拒绝原因1:小游戏无录屏功能,不符合平台要求 2:小游戏录屏时间小于3S,分享按钮点击无反应或提示错误文案,不符合平台要求 3:小游戏录屏时间大于300S,分享按钮点击无反应或无法正常分享录屏,不符合平台要求

不久前写了款小游戏,最近上架了字节跳动小游戏平台。因为这款游戏在微信小游戏里被归类为角色扮演类(微信平台不允许个人开发者上架角色类游戏),因此考虑上架字节跳动,只要有软件著作权即可。

游戏是之前在微信平台已经调试通的,所以只需要按照字节的要求做一点修改就可以了,主要变化如下:

1.支持匿名登录

微信小游戏只需要调用wx.login获得code,再用code去服务端调用code2Session就能拿到unionId了,有了unionId就基本完成登录,后续可以继续调用接口获得昵称,或者干脆让用户自己输入昵称完成角色创建,简单粗暴。
字节小游戏则有一堆的要求,比如支持匿名登录(弹出登录框,用户选择拒绝,也要能以游客的身份登录游戏游玩),又比如资料同步(本机有匿名登录的角色资料,此时又切换到非匿名的账号登录,之前的匿名数据需要覆盖到该账号里,这一条就包含了很多需要处理的后台逻辑)

start() {
        this.labelProcess.string = "游戏初始化中";
        this.login()
    }
login() {
        let _this = this;
        tt.login({
            force: true,
            success(res) {
                // 获取用户信息
                tt.getUserInfo({
                    success(resUser) {
                    	//用户很乖,一路确定,允许登录也允许捞取抖音昵称
                        console.log(`getUserInfo 调用成功`, resUser.userInfo);
                        console.log(res.code);
                        if (res.code) {
                            _this.init(res.code,res.anonymousCode,false, resUser.userInfo.nickName) //false表示非匿名,true表示匿名登录
                        }
                    },
                    fail(resUser) {
                        //用户允许登录了,但不给获取昵称的权限,这时候虽然昵称也是匿名用户,但不能算匿名登录了,因为res.code可以拿到账号APP唯一ID,后续可以在登录的时候把抖音昵称更新到游戏里(如果用户给昵称权限)
                        _this.init(res.code,res.anonymousCode,res,false, '匿名用户')
                    },
                });
            },
            fail(res) {
                //login fail了,表示用户拒绝了登录,启用匿名登录,res.code就是空,此时用res.anonymousCode去后台调用API拿手机识别码,手机识别码这玩意绑定的是手机,可以拿来当匿名角色主键用
                _this.init(res.anonymousCode,true, '匿名用户')
            },
        });
        //以下是微信小游戏的登录比较,要么给res.code,要么别玩,简单粗暴
        // wx.login({
        //     success(res) {
        //         if (res.code) {
        //             //发起网络请求
        //             _this.init(res.code)
        //         } else {
        //             _this.popupError.active = true;
        //             _this.labelError.string = "服务器维护中~\n\n请睡醒一觉再试!";
        //         }
        //     }
        // })
    }

Init方法就是跟后端交互了,拿着res.code或者res.anonymousCode去调用jscode2session接口(注意匿名和非匿名传递的是不同参数),拿到openid或者anonymous_openid作为游戏账号主键即可。
上面的代码有人可能要问了,为啥非匿名登录init的时候也要传res.anonymousCode?不是有res.code吗?这就涉及到上面说的资料同步了,在用res.code换到openId以后,如果这个openId在游戏数据库中不存在,我们需要用这个res.anonymousCode再去换一次anonymous_openid,如果anonymous_openid在我们游戏中存在,表示这个用户之前匿名登录玩过,此时按照平台要求需要把这个匿名资料同步给账号(把数据库里anonymous_openid Update成openid即可)为什么是update不是copy?因为匿名资料同步一次后就要清掉,不然频繁切换账号就能无限复制了是不是。

2.录屏

大坑,只要第一次上抖音基本都会因为相关问题被驳回,无外乎几种情况:1.直接从微信小游戏移植到抖音的,飘的不行文档也没看,直接就没有录屏被打回 2.按照官方文档或者网上找的资料,有功能但是不全被驳回的 3.游戏多场景比较复杂官方例子用了报错的
增加录屏方式:
1.首先在游戏主要场景新增一个按钮,增加点击事件

    btnRecordingClick() {
        if (stateInfo.isRecording == true) { //如果正在录制,把当前录制停止,文本替换成开始录制告诉用户你可以继续录新的了
            this.lblIsRecording.string = '开始录制'
            this.stopRecordScreen()
        }
        else { //如果没在录制,则开始录,文本替换告诉用户你现在正在录
            this.lblIsRecording.string = '停止录制'
            this.startRecordScreen()
        }
    }

因为我的游戏是多场景的,可能在这个场景点击了录制会跳转到其他场景,所以是否在录制的标志位isRecording是放在一个单例的对象里,保证其他场景也能用到,如果是单场景的游戏直接放页面里用this.就行了

开始录制方法(这个方法我是放在单例对象里的,如果是单场景游戏不需要加callback参数,直接写里头就行了)

 public startRecordScreen(callback) {
        if (!this.recorder) this.recorder = tt.getGameRecorderManager();
		//注册开始录屏的回调,其实注册一次就行这里偷懒了每次录屏都注册了一次
        this.recorder.onStart(res => {
            callback("开始录屏")
        })
        //注册录屏结束事件,偷懒*2
        this.recorder.onStop(res => {
            this.isRecording = false
            clearInterval(this.actionRecord);
            if (this.recordingSecond < 4) { //这里用3也可以,不想管边界了就写了4
                callback("录屏小于3s,无法分享!")
            }
            else {
                console.log(res.videoPath);
                this.videoPath = res.videoPath; //如果录屏成功,则获取视频文件的地址
                callback("结束录屏")
            }
        })
		//开始录屏
        this.recorder.start({
            duration: 300
        });
        console.log('start')
        this.isRecording = true
        this.recordingSecond = 0;
        //开启一个计时器,统计录制了几秒钟,如果大于300则停止录制
        this.actionRecord = setInterval(() => {
            this.recordingSecond++
            if (this.recordingSecond >= 300) {
                this.isRecording = false
                clearInterval(this.actionRecord);
            }
        }, 1000);
    }	

结束录制的方法

public stopRecordScreen(callback) {
		
        //结束录屏
        if (this.recorder)
            this.recorder.stop();
    }

注意点: this.recorder.start()如果不给参数默认是10秒,时间到后APP会自动关掉录像并执行onStop注册的方法,如果是多场景游戏也需要把这个事件写到单例对象里去,以免onStop的时候原场景被销毁了导致异常。
start以后需要启动个计数器统计录了几秒,如果启动不到3秒要给提示如果不给提示审核会直接打回。如果录屏时间到达五分钟上限,APP也会自动关掉录像,需要做好逻辑处理不然也会被打回。

3.分享

遇到个坑,原本的代码我是写在录屏的onStop里,类似于 this.recorder.onStop(res => {tt.shareAppMessage(…)})的,在IOS里测试正常但是审核那边却弹不出分享框被打回了,百思不得姐查遍了资料,最后也不知道在哪看到个文章说审核的手机是vivo,在vivo下是有差异的,于是换了个方式在onStop里弹出个对话框,对话框里放个分享按钮,由这个按钮去做分享,代码如下

onbtnShareClick() {
        let _this = this
        tt.shareAppMessage({
            templateId: "xxxxxx", // 替换成通过审核的分享ID
            query: "",
            channel: "video",
            extra: {
                videoPath: stateInfo.videoPath, // 可用录屏得到的视频地址
                videoTopics: ['XXXX']
            },
            success() {
                _this.ndShare.active = false
                _this.showWarning("分享成功");
                console.log("分享成功");
            },
            fail(e) {
                _this.showWarning("取消分享");
                console.log("分享失败");
            },
        });
    }

templateId必须要在控制台通过运营能力-分享设置,通过审核后生成的分享ID,当然也有其他的分享方式具体可看官方文档,channel参数必须写 "video"如果漏掉这个参数分享对话框里是不会有视频的。
另外可以页面上再放一个分享按钮,逻辑也是这个,这样如果是多场景游戏,在其他场景中录屏达到上限结束了(或者是在分享前不小心把这个对话框关掉了),玩家也能使用分享按钮对录好的视频进行分享。

以上,字节的审核非常快从第一次提审到全部修复总共也就半天时间,这点还是比微信给力的。以上代码仅供逻辑参考,可能有bug或者冗余代码(比如计数器里头),请见谅。

有关cocos creator上架字节跳动(抖音)小游戏注意事项(匿名登录、录屏、分享等踩坑记录)的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  3. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  4. ruby - 在匿名 block 中产生 - 2

    我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

  5. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  6. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  7. ruby-on-rails - 事件记录 : Select max of limit - 2

    我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).

  8. Ruby 守护进程导致 ActiveRecord 记录器 IOError - 2

    我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame

  9. ruby-on-rails - 在 Rails 中更高效地查找或创建多条记录 - 2

    我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr

  10. ruby - 我需要从 facebook 游戏中抓取数据——使用 ruby - 2

    修改(澄清问题)我已经花了几天时间试图弄清楚如何从Facebook游戏中抓取特定信息;但是,我遇到了一堵又一堵砖墙。据我所知,主要问题如下。我可以使用Chrome的检查元素工具手动查找我需要的html-它似乎位于iframe中。但是,当我尝试抓取该iframe时,它​​是空的(属性除外):如果我使用浏览器的“查看页面源代码”工具,这与我看到的输出相同。我不明白为什么我看不到iframe中的数据。答案不是它是由AJAX之后添加的。(我知道这既是因为“查看页面源代码”可以读取Ajax添加的数据,也是因为我有b/c我一直等到我可以看到数据页面之后才抓取它,但它仍然不存在)。发生这种情况是因为

随机推荐