Cocos 2.4.7 Building Assets卡住问题

关键词:构建资源、Building Assets、资源刷新、Prefab

背景

下午开发的时候想要构建一下游戏产物,但是一直会卡在资源打包上,白思不得其解,搜了一下官方论坛也没找到相关的问题的解决方法。

定位

reset大法一个个回滚到指定的commit上跑一下构建,直到找到会卡住的那个commit再做分析

// 命令行构建
/Applications/CocosCreator/Creator/2.4.7/CocosCreator.app/Contents/MacOS/CocosCreator --path /Users/7gugu/Documents/gitlab/genshinImpact --build
  1. 切换到项目的根目录
  2. 修改CocosCreator的路径
  3. 修改–path路径(该路径是用于指定在哪个位置创建一个build文件夹并且输出构建产物)
  4. 在命令行中运行代码,看构建是否会卡住

问题

最后定位到是因为有一个图片的meta文件不是被直接删除,而是被覆写,怀疑是这里覆写的时候Cocos没监听到文件变化导致Prefab没更新到,最后导致构建卡住。

解决

彻底删除这个被覆写的meta文件,重新添加图片文件即可修复问题。

延伸

翻查了一下git status,显示的是有一个文件被R052了,后面搜了一下stackoverflow才知道原来这个含义是覆写了52%。

Untitled

https://stackoverflow.com/questions/53056942/git-diff-name-status-what-does-r100-mean

如何解决Merge主分支代码导致本地代码被删除?

背景

之前需求发布的时候,把代码发布到了预发布分支上,但是产品突然发现一个体验问题阻塞了发布,需要先revert掉,等改动完成后再继续发布。

现状

改动完成后,需要同步一下主分支上的代码,在本地解决冲突后再合入主分支。此时会发现Merged后将会把本地代码给删掉。

复盘定位

如图所示,其实问题的根源就是:主分支上的revert没有被撤销,导致合入开发分支的时候本地代码都被revert掉了。

解决办法

简而言之就是,撤销之前的主分支上的revert就可以让主分支上的代码合入到开发分支上又不会把本地代码删掉。相当于告诉git,主分支上这批代码不用被删掉了。具体步骤如下:

  1. 从最新的主分支上创建一个临时分支
  2. 切换到这个临时分支
  3. 在临时分支上revert(撤销)掉之前的revert提交
  4. 将临时分支合入到开发分支上,同步最新主分支代码
  5. 解决问题

如何判断一个JS方法的兼容性?

背景

由于业务的原因,我们的H5页面必须兼容一些比较老的webview版本,因此在开发中难免会遇到不支持的JS语法,因此在这里总结了一个切实可行的方法来提高自己确认接口兼容性的效率。

方法

通过caniuse.com查询方法兼容性

例子

假如需要使用Proxy方法来统一代理所有的接口,但是我不确定系统兼容性,怎么办?

1.访问caniuse.com

2.查询对应的方法名

3.查询iOS的兼容性

4.查询Android的兼容性

5.如果查询到的最低支持版本在需要的版本内则可以使用,另外在使用前最好在方法外套一层try...catch避免出现不支持的意外情况

如何在Apple Watch上添加Loading动画?

背景

去年11月的时候,买了人生第一支Apple Watch,因为内心涌动的软工魂,我又开始琢磨开发一个属于自己的iOS App。之后顺理成章的推出了自己首个WatchApp,《wTodo》一个用于在Apple Watch上管理微软待办事项的工具应用。

无原生组件怎么办?

由于App需要经常性联网,因此不免需要引入Loading态来告知用户当前的运行状态。不巧的是watchOS中提供的组件是阉割后的版本(主要是为用户的续航考虑,如果直接套用手机端那一套组件,watch的续航会受不了的),导致watch端并没有原生的组件能够实现Loading动画,这也让我苦恼了很久。后面下班摸鱼的时候,偶然在stackoverflow上看到了解决办法。

帧动画

如标题所述,在watchOS上只能通过“帧动画”来曲线救国,实现Loading动画。在这里给大家推荐一个来自Stackoverflow高赞中推荐的动画库。

https://github.com/mikeswanson/JBWatchActivityIndicator

该动画库提供了两种帧动画样式,第一种是类原生的Loading帧动画,如果个人比较偏爱原生动画的话,可以使用这个,直接点开“common image”就可以找到不同尺寸,不同帧率的帧动画了;第二种则是通过作者提供的工具,自定义动画的样式,如果想要自定义则需要clone项目到本地,然后通过Xcode打开项目运行到Simulator中即可,调整完参数后,点击生成即可获得符合自己要求的帧动画素材。

如何使用?

这里用类原生动画举例子:

1.从Common Image中选择自己需要的尺寸和帧率

这里我选择Normal Size 15帧的帧动画图片

2. 将帧序列图片拖到WatchKit App的Assets.xcassets文件夹中

3.在StoryBoard中引入Image组件

4.绑定Image组件到Controller上

5.设置Image组件参数

使用startAnimationWithImages后就会开始播放帧序列动画了,因此可以用来看作是动画开始函数。

6.停止播放帧序列动画

总结

至此只要在需要使用Loading动画的地方配置对应的Image组件即可给页面加上Loading态了。实现方法非常简单易用,就是国内文档太少了,只能去社区里找略麻烦了一点。如果希望自己去寻找更多内容的话,可以使用“loading indicator”去搜索,感觉会比Loading Animation说的更准确一些。

PS:

不知不觉就到8月底了,8月的碎碎念我还没来得及写,上周优化了wTodo加入了LoadingIndicator,这周就把加载动画的技术文章尝试沉淀下来了。如果下周有时间的话,我再花时间把碎碎念给肝出来吧。工作日加班真的消耗完我写代码的精力了,到了周末感觉都不想再碰电脑了,只想出门逛一下散散心。(真的不是我咕咕咕啦!)

[SQL] 如何获取昨日每个小时的记录数量?

方法

利用左连接来实现逐小时查询(终于把数据库原理学到的知识用上了😂)

select a.click_date,ifnull(b.total,0) as total from (
SELECT date_format(date_sub(curdate(), interval 1 HOUR),'%Y-%m-%d-%H') as click_date 
union all 
SELECT date_format(date_sub(curdate(), interval 2 HOUR),'%Y-%m-%d-%H') as click_date 
union all 
SELECT date_format(date_sub(curdate(), interval 3 HOUR),'%Y-%m-%d-%H') as click_date 
union all 
SELECT date_format(date_sub(curdate(), interval 4 HOUR),'%Y-%m-%d-%H') as click_date 
union all 
SELECT date_format(date_sub(curdate(), interval 5 HOUR),'%Y-%m-%d-%H') as click_date 
union all 
SELECT date_format(date_sub(curdate(), interval 7 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 8 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 9 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 10 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 11 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 12 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 13 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 14 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 15 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 16 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 17 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 18 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 19 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 20 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 21 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 22 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 23 HOUR),'%Y-%m-%d-%H') as click_date
union all 
SELECT date_format(date_sub(curdate(), interval 24 HOUR),'%Y-%m-%d-%H') as click_date
) a 
left join (
select date_format(createTime,'%Y-%m-%d-%H') as datetime, count(*) as total from `order` where  `status` = 2 group by date_format(createTime,'%Y-%m-%d-%H')
) b 
on a.click_date = b.datetime

infull用于字段的补0,在无数据的时候可以补上0。

dateSub用于减去时间戳的时间

a主要是用来生成逐小时的时间字段

b主要是查询数据

on是用来执行left join的条件

如何在Sequel Ace中快速创建CreateTime与UpdateTime?

前言

本文章主要记录个人的开发经验,产生自最近进行的后端开发。

方法

1.切换到”结构”Tab

2.添加新的字段

3.UpdateTime字段设置为”TimeStamp”, 设置Extra为”on update CURRENT_TIMESTAMP”

4.CreateTime字段设置为”TimeStamp”, Default设置为CURRENT_TIMESTAMP

CURRENT_TIMESTAMP是MySQL自带的一个时间函数,用于生成当前的时间戳”2022-01-05T08:37:17.000Z”。

on update CURRENT_TIMESTAMP表示字段在更新时更新为当前的时间戳。

通过这样子的设定,可以将创建时间和更新时间的操作交由数据库处理,免去了开发者在后端进行额外的处理。

前端如何处理?

new Date("2022-01-05T08:37:17.000Z").getTime())

通过该方法可以快速的转换成距1970 年1 月1 日之间的毫秒数。

【17.0已解决】独立 Watch App 真机调试无网络的问题

问题

现在我正在开发一个独立的 Watch App 应用,但是我发现在模拟器上调试时是可以正常连接网络的。但是当我上传到真机时,就会出现无网络连接的问题。具体的报错如下:

2021-11-20 21:42:27.156338+0800 wTodo WatchKit Extension[1068:2225475] PDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_NSURLErrorFailingURLSessionTaskErrorKey=LocalDataPDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>, NSLocalizedDescription=The Internet connection appears to be offline., _kCFStreamErrorCodeKey=50, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataPDTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>",
    "LocalDataTask <29C198EA-480A-459F-B5B9-421D9C26C7D8>.<3>"
), NSErrorFailingURLStringKey=https://baidu.com, _kCFStreamErrorDomainKey=1, NSErrorFailingURLKey=https://baidu.com}

测试机型

Apple Watch S7

系统版本: 8.1

网络环境: WIFI

原因

在watchOS 8中,GPS版的Apple Watch是不能单独设置APP的网络的,他会镜像iOS侧的App网络设置,比如设置支付宝可以用Wi-Fi,则Watch侧的支付宝App也可以用Wi-Fi。现在陷入的一个僵局仅仅是存在于新推出的独立App,独立App顾名思义就是只存在于Watch侧,iOS侧是没有App的。因此也不存在镜像iOS侧App网络设置的可能。这导致了如果App默认被设置成了不能联网,就会导致用户没法更改App联网状态(因为iOS侧和Watch侧都不能更改App的网络设置)。

解决方案

  1. 彻底关闭手机
    该方法是为了让 Apple Watch 不去同步手机的网络设置(默认关闭网络),促使独立应用能够正常联网,但该方法仅适用于网络连接少或者无的独立应用,比如小说、阅读器等。
  2. 开发 iOS 配套应用
    如果 iOS 侧有配套应用,则用户就可以在 iOS 侧对应用的网络权限做控制,进而实现 Watch 侧应用联网,该方法适合对于网络请求较多的应用。

PS:这个 Bug 的出现真的是很无语,花了我一整天的时间才解决,心累啊😣。

2024/02/17更新:

在watchOS 10.3.1 iOS 17.0的环境下,独立APP已经可以在“蜂窝网络”中看到“仅用于手表的APP”选项,在这里可以单独配置WatchAPP的联网能力。

mapStateToProps 和 mapDispatchToProps的区别

背景

最近开始实习,部门主要使用的是一个内部的跨端框架,这东西本质上还是React Native,只不过是做了一层套娃重命名了标签名称(像极了ReactRouter和ReactRouter-Dom的关系),并且封装📦了自己的一堆私有的API。由于导师就是负责开发这个框架,因此很无奈只能转React,做React开发了(印证了之前的猜想)。

今天这篇文章主要就分享一下我在使用Redux过程中的一些笔记📒或者使用经验。

(本文章不会详细阐述其具体的使用方法,只是会记录一些实际应用的效果)

区别

  1. 相同点
  • 两者都是函数
  • 用于将Redux的Store映射到组件props(入口参数)上
  1. 异同点
函数名称官方描述个人理解
mapStateToProps建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系将Store上的State挂载到props上 (实现了单向数据流)
mapDispatchToProps建立 UI 组件的参数到store.dispatch方法的映射将Store上的Dispatch函数挂载到(调用的时候,相当于是在触发Store上的函数)

实例

(此处引用阮一峰老师提供的“计数器”代码,作为本文章的例子,感谢阮老师的分享🙏)

初始化环境

 import React, { Component } from 'react'
 import ReactDOM from 'react-dom'
 import { createStore } from 'redux'
 import { Provider, connect } from 'react-redux'

创建计数器组件

 class Counter extends Component {
   // 创建渲染器
   render() {
     // 从入口参数上获取mapState和mapDispatch挂载的属性和方法
     const { value, onIncreaseClick } = this.props
     // 返回JSX
     return (
       <div>
         <span>{value}</span>
         <button onClick={onIncreaseClick}>Increase</button>
       </div>
    )
  }
 }

创建Action

Action说明了Redux中可以做什么动作。

 const increaseAction = { type: 'increase' }

有点类似于Java中的抽象类的概念

创建Reducer

Reducer说明了Action做什么动作。

 function counter(state = { count: 0 }, action) {
   // 读取Redux中管理的count变量
   const count = state.count
   // 检测dispatch触发了哪一个动作
   switch (action.type) {
       // 触发"增加"动作
     case 'increase':
       // 将值+1并返回一个state到Redux的Store中
       return { count: count + 1 }
     default:
       return state
  }
 }

有点类似于Java中的实现类

PS:

Action和Reducer的关系,在我看来有点类似于抽象实现的关系,不知道🤷‍♂️对不对,以后再继续深入探究一下。

创建Store

 const store = createStore(counter)

实现挂载函数

 // Redux的角度: 将Redux管理的全局State(Store)上的值挂载到了组件上
 // 组件的角度: 获取(订阅)了Redux管理的全局State(Store)上的值
 function mapStateToProps(state) {
   return {
     value: state.count
  }
 }
 ​
 // Redux的角度: 将操作全局State(Store)上的方法挂载到了组件上
 // 组件的角度: 获取了管理的全局State(Store)的方法
 function mapDispatchToProps(dispatch) {
   return {
     onIncreaseClick: () => dispatch(increaseAction)
  }
 }

挂载到组件上

 // Connected Component
 const App = connect(
   mapStateToProps,
   mapDispatchToProps
 )(Counter)

页面渲染

 ReactDOM.render(
   <Provider store={store}>
     <App />
   </Provider>,
   document.getElementById('root')
 )

流程图例

Redux与组件之间是如何传递reducer和state以及组件如何dispatch reducer的
  • 通过mapStateToProps将全局State挂载到了组件的Props上,组件因此可以直接读取到Store中管理的state值。
  • 通过mapDispatchToProps将管理全局State的方法挂载到了组件的Props上,组件就可以从入参中获取操作方法,进而在组件中dispatch(触发)State中的Reducer进而修改State中的值。

结尾

这篇文章主要是梳理了一下本周解决的一个知识点,加深自私点在脑海中的印象。文章中阐述不清楚的点,欢迎透过邮箱📮与发起讨论,希望透过讨论也能让你明白这个过程发生了什么。

延伸资料

  1. React-Redux 的用法(中文)
  2. 使用mapStateToProps导出数据(官方英文)
  3. 使用mapDispatchToProps触发Actions(官方英文)
  4. 计算器代码(Github)PS:建议可以先不看🙈这两个函数入参的描述,因为很容易把人搞晕😵,可以着重看看[1]中,阮一峰老师提供的实例,搭配[2] [3]思考🤔一下,相信你很快也能get到这个函数在干什么,有什么用🤷‍♂️。

React脚手架V1.0

背景

最近开始捯饬React,想着学会了React就能把React Native给学习了,然后就能写App。为了能够在React Native上少踩坑,我搭建这个脚手架之后,准备开发一个博文发布系统,作为DEMO练练手。

依赖构成

  1. React核心
  2. React-router-dom 路由
  3. Redux 状态管理
  4. ESlint 代码格式管理
  5. Vite 打包工具
  6. Axios 网络请求库
  7. Antd UI库

搭建流程

1. 初始化React

1.1 模板初始化

我们可以直接使用Vite自带的模板,初始化React,在本教程中我使用的是template指令来初始化项目。

运行以下指令初始化项目

 # npm 6.0
 npm init @vitejs/app gublog(项目名称) --template react(模板名称)
 ​
 # npm 7.0+ (需要额外的双横线)
 npm init @vitejs/app gublog(项目名称) --(额外的双横线) --template react(模板名称)

运行指令后,npm将创建一个以项目名称命名的文件夹📁,来初始化项目。

初始化完成后的文件树结构为:

 root
 └── gublog(项目名称)
    ├── dist
    ├── node_modules
    ├── src
    ├── index.html
    ├── package-lock.json
    ├── package.json
    └── vite.config.js

1.2 启动环境

  1. cd 项目文件夹
  2. npm install #下载依赖
  3. npm run dev #启动开发环境
环境启动成功

出现上图的网页时,我们就已经搭建出Vite + React的开发环境

2. 安装React-router-dom

为了能够实现无刷新切换页面,此处我的脚手架也会引入官方提供的Router来做前端路由管理。

2.1 安装依赖

运行指令安装路由组件

 npm install react-router-dom --save-dev

2.2 注意⚠️

React-Router-Dom就是React Router V4的浏览器版本,原来文档的Router要被替换成BrowserRouter才能使用。为了不使用方面,我们可以使用as关键字来修改别名。

 import 'BrowserRouter' as 'Router' from 'react-router-dom'

这里我真的很想吐槽,搞一个React-Router就好了嘛🤷‍♂️,干嘛还搞一个React-Router-Dom。这东西本质上就是套娃再封装📦。而且最近的V4版本还舍弃了旧的Router标签,改成了BrowserRouter。真的是NT。

解决方法来源:

3. 安装Redux

安装Redux主要是为了使得组件的耦合降低,使得更容易的管理状态,因此被打包到脚手架当中。

3.1 安装依赖

 npm install redux --save-dev

4. 安装ESlint

4.1 安装依赖

 npm install eslint --save-dev

5. 安装Axios网络请求库

5.1 安装依赖

 npm install axios --save-dev

6. 安装AntdUI库

AntdUI是阿里针对React设计的一个UI库,开箱即用,有丰富的文档。(而且还能改颜色)

6.1 安装依赖

 npm install redux --save-dev

6.2 引入环境

  1. main.css中添加import 'antd/dist/antd.css'导入CSS库
  2. Main.jsx的代码头部添加import {组件名称} from antd导入组件

结尾

这篇文章就此告一段落。Axios和ESlint的使用我还在摸索中,后面还会继续更新,敬请期待。目前在使用这个脚手架重写GuBlog日志程序当中,后面时机成熟后也会同新版本的脚手架文章一同推出。

npm 安装、删除依赖命令

npm安装依赖

  • npm install xxx 利用 npm 安装xxx依赖到当前命令行所在目录
  • npm install xxx -g 利用npm安装全局依赖xxx
  • npm install xxx –save 安装并写入package.json的”dependencies”中
  • npm install xxx –save-dev 安装并写入package.json的”devDependencies”中

npm删除依赖

  • npm uninstall xxx 删除xxx依赖
  • npm uninstall xxx -g 删除全局依赖xxx