[adapters] 增加小游戏适配部分源码

This commit is contained in:
SmallMain
2024-10-16 17:12:08 +08:00
parent 887d4a96c9
commit 07bf3b7a96
345 changed files with 38447 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
const _global = GameGlobal;
const adapter = _global.__globalAdapter = {};
Object.assign(adapter, {
init () {
require('./wrapper/builtin');
_global.DOMParser = require('../../common/xmldom/dom-parser').DOMParser;
require('./wrapper/unify');
require('./wrapper/fs-utils');
require('../../common/engine/globalAdapter');
require('./wrapper/systemInfo');
},
adaptEngine () {
require('./wrapper/error-reporter');
require('../../common/engine');
require('./wrapper/engine');
require('./wrapper/sub-context-adapter');
},
});

View File

@@ -0,0 +1,11 @@
let moduleMap = {
// tail
};
window.__cocos_require__ = function (moduleName) {
let func = moduleMap[moduleName];
if (!func) {
throw new Error(`cannot find module ${moduleName}`);
}
return func();
};

View File

@@ -0,0 +1,3 @@
{
"main": "cocos2d-js-min.js"
}

View File

@@ -0,0 +1,9 @@
{
"provider": "wx7095f7fa398a2f30",
"signature": [
{
"path": "cocos2d-js-min.js",
"md5": ""
}
]
}

View File

@@ -0,0 +1,20 @@
require('adapter-js-path');
__globalAdapter.init();
require('cocos2d-js-path');
require('physics-js-path');
__globalAdapter.adaptEngine();
require('./ccRequire');
require('./src/settings');
// Introduce Cocos Service here
require('./main'); // TODO: move to common
// Adjust devicePixelRatio
cc.view._maxPixelRatio = 4;
if (cc.sys.platform !== cc.sys.WECHAT_GAME_SUB) {
// Release Image objects after uploaded gl texture
cc.macro.CLEANUP_IMAGE_CACHE = true;
}
window.boot();

View File

@@ -0,0 +1,17 @@
{
"deviceOrientation": "portrait",
"openDataContext": "",
"networkTimeout": {
"request": 5000,
"connectSocket": 5000,
"uploadFile": 5000,
"downloadFile": 5000
},
"plugins": {
"cocos": {
"provider": "wx7095f7fa398a2f30",
"version": "2.4.15",
"path": "cocos"
}
}
}

View File

@@ -0,0 +1,67 @@
window.boot = function () {
var settings = window._CCSettings;
window._CCSettings = undefined;
var onStart = function () {
cc.view.enableRetina(true);
cc.view.resizeWithBrowserSize(true);
var launchScene = settings.launchScene;
// load scene
cc.director.loadScene(launchScene, null,
function () {
console.log('Success to load scene: ' + launchScene);
}
);
};
var isSubContext = (cc.sys.platform === cc.sys.WECHAT_GAME_SUB);
var option = {
id: 'GameCanvas',
debugMode: settings.debug ? cc.debug.DebugMode.INFO : cc.debug.DebugMode.ERROR,
showFPS: !isSubContext && settings.debug,
frameRate: 60,
groupList: settings.groupList,
collisionMatrix: settings.collisionMatrix,
}
cc.assetManager.init({
bundleVers: settings.bundleVers,
subpackages: settings.subpackages,
remoteBundles: settings.remoteBundles,
server: settings.server,
subContextRoot: settings.subContextRoot
});
var RESOURCES = cc.AssetManager.BuiltinBundleName.RESOURCES;
var INTERNAL = cc.AssetManager.BuiltinBundleName.INTERNAL;
var MAIN = cc.AssetManager.BuiltinBundleName.MAIN;
var START_SCENE = cc.AssetManager.BuiltinBundleName.START_SCENE;
var bundleRoot = [INTERNAL];
settings.hasResourcesBundle && bundleRoot.push(RESOURCES);
settings.hasStartSceneBundle && bundleRoot.push(MAIN);
var count = 0;
function cb (err) {
if (err) return console.error(err.message, err.stack);
count++;
if (count === bundleRoot.length + 1) {
// if there is start-scene bundle. should load start-scene bundle in the last stage
// Otherwise the main bundle should be the last
cc.assetManager.loadBundle(settings.hasStartSceneBundle ? START_SCENE : MAIN, function (err) {
if (!err) cc.game.run(option, onStart);
});
}
}
// load plugins
cc.assetManager.loadScript(settings.jsList.map(function (x) { return 'src/' + x;}), cb);
// load bundles
for (var i = 0; i < bundleRoot.length; i++) {
cc.assetManager.loadBundle(bundleRoot[i], cb);
}
};

View File

@@ -0,0 +1,34 @@
{
"description": "项目配置文件。",
"miniprogramRoot": "./",
"setting": {
"urlCheck": true,
"es6": true,
"postcss": true,
"minified": false,
"newFeature": false
},
"compileType": "game",
"libVersion": "game",
"appid": "",
"projectname": "",
"condition": {
"search": {
"current": -1,
"list": []
},
"conversation": {
"current": -1,
"list": []
},
"game": {
"currentL": -1,
"list": [],
"current": -1
},
"miniprogram": {
"current": -1,
"list": []
}
}
}

View File

@@ -0,0 +1,205 @@
import HTMLAudioElement from './HTMLAudioElement'
const HAVE_NOTHING = 0
const HAVE_METADATA = 1
const HAVE_CURRENT_DATA = 2
const HAVE_FUTURE_DATA = 3
const HAVE_ENOUGH_DATA = 4
let SN_SEED = 1
const _innerAudioContextMap = {}
export default class Audio extends HTMLAudioElement {
constructor(url) {
super()
this._$sn = SN_SEED++;
this.HAVE_NOTHING = HAVE_NOTHING
this.HAVE_METADATA = HAVE_METADATA
this.HAVE_CURRENT_DATA = HAVE_CURRENT_DATA
this.HAVE_FUTURE_DATA = HAVE_FUTURE_DATA
this.HAVE_ENOUGH_DATA = HAVE_ENOUGH_DATA
this.readyState = HAVE_NOTHING
const innerAudioContext = wx.createInnerAudioContext()
_innerAudioContextMap[this._$sn] = innerAudioContext
this._canplayEvents = [
'load',
'loadend',
'canplay',
'canplaythrough',
'loadedmetadata'
]
innerAudioContext.onCanplay(() => {
this._loaded = true
this.readyState = this.HAVE_CURRENT_DATA
this._canplayEvents.forEach((type) => {
this.dispatchEvent({ type: type })
})
})
innerAudioContext.onPlay(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'play' })
})
innerAudioContext.onPause(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'pause' })
})
innerAudioContext.onEnded(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
if (_innerAudioContextMap[this._$sn].loop === false) {
this.dispatchEvent({ type: 'ended' })
}
this.readyState = HAVE_ENOUGH_DATA
})
innerAudioContext.onError(() => {
this._paused = _innerAudioContextMap[this._$sn].paused
this.dispatchEvent({ type: 'error' })
})
if (url) {
this.src = url
} else {
this._src = ''
}
this._loop = innerAudioContext.loop
this._autoplay = innerAudioContext.autoplay
this._paused = innerAudioContext.paused
this._volume = innerAudioContext.volume
this._muted = false;
}
addEventListener(type, listener, options = {}) {
super.addEventListener(type, listener, options)
type = String(type).toLowerCase()
if (this._loaded && this._canplayEvents.indexOf(type) !== -1) {
this.dispatchEvent({ type: type })
}
}
load() {
// console.warn('HTMLAudioElement.load() is not implemented.')
// weixin doesn't need call load() manually
}
play() {
_innerAudioContextMap[this._$sn].play()
}
resume() {
_innerAudioContextMap[this._$sn].resume()
}
pause() {
_innerAudioContextMap[this._$sn].pause()
}
stop() {
_innerAudioContextMap[this._$sn].stop()
}
destroy() {
_innerAudioContextMap[this._$sn].destroy()
}
canPlayType(mediaType = '') {
if (typeof mediaType !== 'string') {
return ''
}
if (mediaType.indexOf('audio/mpeg') > -1 || mediaType.indexOf('audio/mp4')) {
return 'probably'
}
return ''
}
get currentTime() {
return _innerAudioContextMap[this._$sn].currentTime
}
set currentTime(value) {
_innerAudioContextMap[this._$sn].seek(value)
}
get duration () {
return _innerAudioContextMap[this._$sn].duration
}
get src() {
return this._src
}
set src(value) {
this._src = value
this._loaded = false
this.readyState = this.HAVE_NOTHING
const innerAudioContext = _innerAudioContextMap[this._$sn]
innerAudioContext.src = value
}
get loop() {
return this._loop
}
set loop(value) {
this._loop = value
_innerAudioContextMap[this._$sn].loop = value
}
get autoplay() {
return this.autoplay
}
set autoplay(value) {
this._autoplay = value
_innerAudioContextMap[this._$sn].autoplay = value
}
get paused() {
return this._paused;
}
get volume() {
return this._volume;
}
set volume(value) {
this._volume = value;
if (!this._muted) {
_innerAudioContextMap[this._$sn].volume = value;
}
}
get muted() {
return this._muted;
}
set muted(value) {
this._muted = value;
if (value) {
_innerAudioContextMap[this._$sn].volume = 0;
} else {
_innerAudioContextMap[this._$sn].volume = this._volume;
}
}
cloneNode() {
const newAudio = new Audio()
newAudio.loop = this.loop
newAudio.autoplay = this.autoplay
newAudio.src = this.src
return newAudio
}
}

View File

@@ -0,0 +1,64 @@
// import HTMLCanvasElement from './HTMLCanvasElement'
import { innerWidth, innerHeight } from './WindowProperties'
let hasModifiedCanvasPrototype = false
let hasInit2DContextConstructor = false
let hasInitWebGLContextConstructor = false
export default function Canvas() {
const canvas = wx.createCanvas()
canvas.type = 'canvas'
// canvas.__proto__.__proto__.__proto__ = new HTMLCanvasElement()
const _getContext = canvas.getContext
canvas.getBoundingClientRect = () => {
const ret = {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
}
return ret
}
canvas.style = {
top: '0px',
left: '0px',
width: innerWidth + 'px',
height: innerHeight + 'px',
}
canvas.addEventListener = function (type, listener, options = {}) {
// console.log('canvas.addEventListener', type);
document.addEventListener(type, listener, options);
}
canvas.removeEventListener = function (type, listener) {
// console.log('canvas.removeEventListener', type);
document.removeEventListener(type, listener);
}
canvas.dispatchEvent = function (event = {}) {
console.log('canvas.dispatchEvent' , event.type, event);
// nothing to do
}
Object.defineProperty(canvas, 'clientWidth', {
enumerable: true,
get: function get() {
return innerWidth
}
})
Object.defineProperty(canvas, 'clientHeight', {
enumerable: true,
get: function get() {
return innerHeight
}
})
return canvas
}

View File

@@ -0,0 +1,10 @@
import Node from './Node'
export default class Element extends Node {
className = ''
children = []
constructor() {
super()
}
}

View File

@@ -0,0 +1,14 @@
import {noop} from './util'
export default class Event {
cancelBubble = false
cancelable = false
target = null
timestampe = Date.now()
preventDefault = noop
stopPropagation = noop
constructor(type) {
this.type = type
}
}

View File

@@ -0,0 +1,5 @@
export default class MouseEvent {
constructor() {
}
}

View File

@@ -0,0 +1,32 @@
import { noop } from '../util/index.js'
export default class TouchEvent {
touches = []
targetTouches = []
changedTouches = []
preventDefault = noop
stopPropagation = noop
constructor(type) {
this.type = type
this.target = window.canvas
this.currentTarget = window.canvas
}
}
function touchEventHandlerFactory(type) {
return (event) => {
const touchEvent = new TouchEvent(type)
touchEvent.touches = event.touches
touchEvent.targetTouches = Array.prototype.slice.call(event.touches)
touchEvent.changedTouches = event.changedTouches
touchEvent.timeStamp = event.timeStamp
document.dispatchEvent(touchEvent)
}
}
wx.onTouchStart(touchEventHandlerFactory('touchstart'))
wx.onTouchMove(touchEventHandlerFactory('touchmove'))
wx.onTouchEnd(touchEventHandlerFactory('touchend'))
wx.onTouchCancel(touchEventHandlerFactory('touchcancel'))

View File

@@ -0,0 +1,2 @@
export TouchEvent from './TouchEvent'
export MouseEvent from './MouseEvent'

View File

@@ -0,0 +1,57 @@
const _events = new WeakMap()
export default class EventTarget {
constructor() {
_events.set(this, {})
}
addEventListener(type, listener, options = {}) {
let events = _events.get(this)
if (!events) {
events = {}
_events.set(this, events)
}
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
if (options.capture) {
// console.warn('EventTarget.addEventListener: options.capture is not implemented.')
}
if (options.once) {
// console.warn('EventTarget.addEventListener: options.once is not implemented.')
}
if (options.passive) {
// console.warn('EventTarget.addEventListener: options.passive is not implemented.')
}
}
removeEventListener(type, listener) {
const events = _events.get(this)
if (events) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
}
}
dispatchEvent(event = {}) {
const listeners = _events.get(this)[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}

View File

@@ -0,0 +1,7 @@
/*
* TODO 使用 wx.readFile 来封装 FileReader
*/
export default class FileReader {
construct() {
}
}

View File

@@ -0,0 +1,7 @@
import HTMLMediaElement from './HTMLMediaElement'
export default class HTMLAudioElement extends HTMLMediaElement {
constructor() {
super('audio')
}
}

View File

@@ -0,0 +1,20 @@
// import HTMLElement from './HTMLElement';
// export default class HTMLCanvasElement extends HTMLElement
// {
// constructor(){
// super('canvas')
// }
// };
import Canvas from './Canvas'
import HTMLElement from './HTMLElement'
GameGlobal.screencanvas = GameGlobal.screencanvas || new Canvas();
const canvas = GameGlobal.screencanvas;
const canvasConstructor = canvas.constructor;
// canvasConstructor.__proto__.__proto__ = new HTMLElement();
export default canvasConstructor;

View File

@@ -0,0 +1,54 @@
import Element from './Element'
import { noop } from './util/index.js'
import { innerWidth, innerHeight } from './WindowProperties'
export default class HTMLElement extends Element {
className = ''
childern = []
style = {
width: `${innerWidth}px`,
height: `${innerHeight}px`
}
insertBefore = noop
innerHTML = ''
constructor(tagName = '') {
super()
this.tagName = tagName.toUpperCase()
}
setAttribute(name, value) {
this[name] = value
}
getAttribute(name) {
return this[name]
}
get clientWidth() {
const ret = parseInt(this.style.fontSize, 10) * this.innerHTML.length
return Number.isNaN(ret) ? 0 : ret
}
get clientHeight() {
const ret = parseInt(this.style.fontSize, 10)
return Number.isNaN(ret) ? 0 : ret
}
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: innerWidth,
height: innerHeight
}
}
focus() {
}
}

View File

@@ -0,0 +1,16 @@
// import HTMLElement from './HTMLElement';
// export default class HTMLImageElement extends HTMLElement
// {
// constructor(){
// super('img')
// }
// };
import HTMLElement from './HTMLElement'
const imageConstructor = wx.createImage().constructor;
// imageConstructor.__proto__.__proto__ = new HTMLElement();
export default imageConstructor;

View File

@@ -0,0 +1,25 @@
import HTMLElement from './HTMLElement'
export default class HTMLMediaElement extends HTMLElement {
constructor(type) {
super(type)
}
addTextTrack() {
}
captureStream() {
}
fastSeek() {
}
load() {
}
pause() {
}
play() {
}
}

View File

@@ -0,0 +1,8 @@
import HTMLMediaElement from './HTMLMediaElement';
export default class HTMLVideoElement extends HTMLMediaElement
{
constructor(){
super('video')
}
};

View File

@@ -0,0 +1,9 @@
import HTMLImageElement from './HTMLImageElement'
export default function() {
const image = wx.createImage();
// image.__proto__.__proto__.__proto__ = new HTMLImageElement();
return image;
};

View File

@@ -0,0 +1,5 @@
export default class ImageBitmap {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,34 @@
import EventTarget from './EventTarget.js'
export default class Node extends EventTarget {
constructor() {
super()
}
childNodes = []
appendChild(node) {
this.childNodes.push(node)
// if (node instanceof Node) {
// this.childNodes.push(node)
// } else {
// throw new TypeError('Failed to executed \'appendChild\' on \'Node\': parameter 1 is not of type \'Node\'.')
// }
}
cloneNode() {
const copyNode = Object.create(this)
Object.assign(copyNode, this)
return copyNode
}
removeChild(node) {
const index = this.childNodes.findIndex((child) => child === node)
if (index > -1) {
return this.childNodes.splice(index, 1)
}
return null
}
}

View File

@@ -0,0 +1,5 @@
export default class WebGLRenderingContext {
constructor() {
// TODO
}
}

View File

@@ -0,0 +1,87 @@
const _socketTask = new WeakMap()
export default class WebSocket {
static CONNECTING = 0 // The connection is not yet open.
static OPEN = 1 // The connection is open and ready to communicate.
static CLOSING = 2 // The connection is in the process of closing.
static CLOSED = 3 // The connection is closed or couldn't be opened.
binaryType = '' // TODO 更新 binaryType
bufferedAmount = 0 // TODO 更新 bufferedAmount
extensions = ''
onclose = null
onerror = null
onmessage = null
onopen = null
protocol = '' // TODO 小程序内目前获取不到,实际上需要根据服务器选择的 sub-protocol 返回
readyState = 3
constructor(url, protocols = []) {
if (typeof url !== 'string' || !(/(^ws:\/\/)|(^wss:\/\/)/).test(url)) {
throw new TypeError(`Failed to construct 'WebSocket': The URL '${url}' is invalid`)
}
this.url = url
this.readyState = WebSocket.CONNECTING
const socketTask = wx.connectSocket({
url,
protocols: Array.isArray(protocols) ? protocols : [protocols],
tcpNoDelay: true
})
_socketTask.set(this, socketTask)
socketTask.onClose((res) => {
this.readyState = WebSocket.CLOSED
if (typeof this.onclose === 'function') {
this.onclose(res)
}
})
socketTask.onMessage((res) => {
if (typeof this.onmessage === 'function') {
this.onmessage(res)
}
})
socketTask.onOpen(() => {
this.readyState = WebSocket.OPEN
if (typeof this.onopen === 'function') {
this.onopen()
}
})
socketTask.onError((res) => {
if (typeof this.onerror === 'function') {
this.onerror(new Error(res.errMsg))
}
})
return this
}
close(code, reason) {
this.readyState = WebSocket.CLOSING
const socketTask = _socketTask.get(this)
socketTask.close({
code,
reason
})
}
send(data) {
if (typeof data !== 'string' && !(data instanceof ArrayBuffer) && !ArrayBuffer.isView(data)) {
throw new TypeError(`Failed to send message: The data ${data} is invalid`)
}
const socketTask = _socketTask.get(this)
socketTask.send({
data
})
}
}

View File

@@ -0,0 +1,21 @@
const { screenWidth, screenHeight, devicePixelRatio } = wx.getSystemInfoSync()
export const innerWidth = screenWidth
export const innerHeight = screenHeight
export { devicePixelRatio }
export const screen = {
width: screenWidth,
height: screenHeight,
availWidth: innerWidth,
availHeight: innerHeight,
availLeft: 0,
availTop: 0,
}
export const performance = {
now: Date.now
};
export const ontouchstart = null;
export const ontouchmove = null;
export const ontouchend = null;

View File

@@ -0,0 +1,5 @@
export default function(file) {
const worker = wx.createWorker(file);
return worker;
};

View File

@@ -0,0 +1,172 @@
import EventTarget from './EventTarget.js'
const _url = new WeakMap()
const _method = new WeakMap()
const _requestHeader = new WeakMap()
const _responseHeader = new WeakMap()
const _requestTask = new WeakMap()
function _triggerEvent(type, ...args) {
if (typeof this[`on${type}`] === 'function') {
this[`on${type}`].apply(this, args)
}
}
function _changeReadyState(readyState) {
this.readyState = readyState
_triggerEvent.call(this, 'readystatechange')
}
export default class XMLHttpRequest extends EventTarget {
// TODO 没法模拟 HEADERS_RECEIVED 和 LOADING 两个状态
static UNSEND = 0
static OPENED = 1
static HEADERS_RECEIVED = 2
static LOADING = 3
static DONE = 4
timeout = 0;
/*
* TODO 这一批事件应该是在 XMLHttpRequestEventTarget.prototype 上面的
*/
onabort = null
onerror = null
onload = null
onloadstart = null
onprogress = null
ontimeout = null
onloadend = null
onreadystatechange = null
readyState = 0
response = null
responseText = null
responseType = ''
responseXML = null
status = 0
statusText = ''
upload = {}
withCredentials = false
constructor() {
super();
_requestHeader.set(this, {
'content-type': 'application/x-www-form-urlencoded'
})
_responseHeader.set(this, {})
}
abort() {
const myRequestTask = _requestTask.get(this)
if (myRequestTask) {
myRequestTask.abort()
}
}
getAllResponseHeaders() {
const responseHeader = _responseHeader.get(this)
return Object.keys(responseHeader).map((header) => {
return `${header}: ${responseHeader[header]}`
}).join('\n')
}
getResponseHeader(header) {
return _responseHeader.get(this)[header]
}
open(method, url/* async, user, password 这几个参数在小程序内不支持*/) {
_method.set(this, method)
_url.set(this, url)
_changeReadyState.call(this, XMLHttpRequest.OPENED)
}
overrideMimeType() {
}
send(data = '') {
if (this.readyState !== XMLHttpRequest.OPENED) {
throw new Error("Failed to execute 'send' on 'XMLHttpRequest': The object's state must be OPENED.")
} else {
let myRequestTask = wx.request({
data,
url: _url.get(this),
method: _method.get(this),
header: _requestHeader.get(this),
dataType: 'other',
responseType: this.responseType === 'arraybuffer' ? 'arraybuffer' : 'text',
timeout: this.timeout || undefined,
success: ({ data, statusCode, header }) => {
this.status = statusCode
_responseHeader.set(this, header)
_triggerEvent.call(this, 'loadstart')
_changeReadyState.call(this, XMLHttpRequest.HEADERS_RECEIVED)
_changeReadyState.call(this, XMLHttpRequest.LOADING)
switch (this.responseType) {
case 'json':
this.responseText = data;
try {
this.response = JSON.parse(data);
}
catch (e) {
this.response = null;
}
break;
case '':
case 'text':
this.responseText = this.response = data;
break;
case 'arraybuffer':
this.response = data;
this.responseText = '';
var bytes = new Uint8Array(data);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
this.responseText += String.fromCharCode(bytes[i]);
}
break;
default:
this.response = null;
}
_changeReadyState.call(this, XMLHttpRequest.DONE)
_triggerEvent.call(this, 'load')
_triggerEvent.call(this, 'loadend')
},
fail: ({ errMsg }) => {
// TODO 规范错误
if (errMsg.indexOf('abort') !== -1) {
_triggerEvent.call(this, 'abort')
} else if (errMsg.indexOf('timeout') !== -1) {
_triggerEvent.call(this, 'timeout')
} else {
_triggerEvent.call(this, 'error', errMsg)
}
_triggerEvent.call(this, 'loadend')
}
})
_requestTask.set(this, myRequestTask);
}
}
setRequestHeader(header, value) {
const myHeader = _requestHeader.get(this)
myHeader[header] = value
_requestHeader.set(this, myHeader)
}
addEventListener(type, listener) {
if (typeof listener === 'function') {
let _this = this
let event = { target: _this }
this['on' + type] = function (event) {
listener.call(_this, event)
}
}
}
}

View File

@@ -0,0 +1,127 @@
import * as window from './window'
import HTMLElement from './HTMLElement'
import HTMLVideoElement from './HTMLVideoElement'
import Image from './Image'
import Audio from './Audio'
import Canvas from './Canvas'
import './EventIniter/index.js'
const events = {}
const document = {
readyState: 'complete',
visibilityState: 'visible',
documentElement: window,
hidden: false,
style: {},
location: window.location,
ontouchstart: null,
ontouchmove: null,
ontouchend: null,
head: new HTMLElement('head'),
body: new HTMLElement('body'),
createElement(tagName) {
if (tagName === 'canvas') {
return new Canvas()
} else if (tagName === 'audio') {
return new Audio()
} else if (tagName === 'img') {
return new Image()
}else if (tagName === 'video') {
return new HTMLVideoElement()
}
return new HTMLElement(tagName)
},
createElementNS(nameSpace, tagName) {
return this.createElement(tagName);
},
getElementById(id) {
if (id === window.canvas.id) {
return window.canvas
}
return null
},
getElementsByTagName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [window.canvas]
}
return []
},
getElementsByName(tagName) {
if (tagName === 'head') {
return [document.head]
} else if (tagName === 'body') {
return [document.body]
} else if (tagName === 'canvas') {
return [window.canvas]
}
return []
},
querySelector(query) {
if (query === 'head') {
return document.head
} else if (query === 'body') {
return document.body
} else if (query === 'canvas') {
return window.canvas
} else if (query === `#${window.canvas.id}`) {
return window.canvas
}
return null
},
querySelectorAll(query) {
if (query === 'head') {
return [document.head]
} else if (query === 'body') {
return [document.body]
} else if (query === 'canvas') {
return [window.canvas]
}
return []
},
addEventListener(type, listener) {
if (!events[type]) {
events[type] = []
}
events[type].push(listener)
},
removeEventListener(type, listener) {
const listeners = events[type]
if (listeners && listeners.length > 0) {
for (let i = listeners.length; i--; i > 0) {
if (listeners[i] === listener) {
listeners.splice(i, 1)
break
}
}
}
},
dispatchEvent(event) {
const listeners = events[event.type]
if (listeners) {
for (let i = 0; i < listeners.length; i++) {
listeners[i](event)
}
}
}
}
export default document

View File

@@ -0,0 +1,55 @@
import * as _window from './window'
import document from './document'
import HTMLElement from './HTMLElement'
const global = GameGlobal
function inject () {
_window.document = document;
_window.addEventListener = (type, listener) => {
_window.document.addEventListener(type, listener)
}
_window.removeEventListener = (type, listener) => {
_window.document.removeEventListener(type, listener)
}
_window.dispatchEvent = _window.document.dispatchEvent;
const { platform } = wx.getSystemInfoSync()
// 开发者工具无法重定义 window
if (typeof __devtoolssubcontext === 'undefined' && platform === 'devtools') {
for (const key in _window) {
const descriptor = Object.getOwnPropertyDescriptor(global, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(window, key, {
value: _window[key]
})
}
}
for (const key in _window.document) {
const descriptor = Object.getOwnPropertyDescriptor(global.document, key)
if (!descriptor || descriptor.configurable === true) {
Object.defineProperty(global.document, key, {
value: _window.document[key]
})
}
}
window.parent = window
} else {
for (const key in _window) {
global[key] = _window[key]
}
global.window = _window
window = global
window.top = window.parent = window
}
}
if (!GameGlobal.__isAdapterInjected) {
GameGlobal.__isAdapterInjected = true
inject()
}

View File

@@ -0,0 +1,31 @@
const localStorage = {
get length() {
const { keys } = wx.getStorageInfoSync()
return keys.length
},
key(n) {
const { keys } = wx.getStorageInfoSync()
return keys[n]
},
getItem(key) {
return wx.getStorageSync(key)
},
setItem(key, value) {
return wx.setStorageSync(key, value)
},
removeItem(key) {
wx.removeStorageSync(key)
},
clear() {
wx.clearStorageSync()
}
}
export default localStorage

View File

@@ -0,0 +1,7 @@
const location = {
href: 'game.js',
reload() {
}
}
export default location

View File

@@ -0,0 +1,38 @@
import { noop } from './util/index.js'
// TODO 需要 wx.getSystemInfo 获取更详细信息
const systemInfo = wx.getSystemInfoSync()
console.log(systemInfo)
const system = systemInfo.system;
const platform = systemInfo.platform;
const language = systemInfo.language;
const wechatVersioin = systemInfo.version;
const android = system ? system.toLowerCase().indexOf('android') !== -1 : false;
const uaDesc = android ? `Android; CPU ${system}` : `iPhone; CPU iPhone OS ${system} like Mac OS X`;
const ua = `Mozilla/5.0 (${uaDesc}) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E8301 MicroMessenger/${wechatVersioin} MiniGame NetType/WIFI Language/${language}`;
const navigator = {
platform,
language: language,
appVersion: `5.0 (${uaDesc}) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1`,
userAgent: ua,
onLine: true, // TODO 用 wx.getNetworkStateChange 和 wx.onNetworkStateChange 来返回真实的状态
// TODO 用 wx.getLocation 来封装 geolocation
geolocation: {
getCurrentPosition: noop,
watchPosition: noop,
clearWatch: noop
}
}
if (wx.onNetworkStatusChange) {
wx.onNetworkStatusChange(function(event){
navigator.onLine = event.isConnected;
});
}
export default navigator

View File

@@ -0,0 +1 @@
export function noop() {}

View File

@@ -0,0 +1,41 @@
import Canvas from './Canvas'
export navigator from './navigator'
export XMLHttpRequest from './XMLHttpRequest'
export WebSocket from './WebSocket'
export Image from './Image'
export ImageBitmap from './ImageBitmap'
export Audio from './Audio'
export FileReader from './FileReader'
export HTMLElement from './HTMLElement'
export HTMLImageElement from './HTMLImageElement'
export HTMLCanvasElement from './HTMLCanvasElement'
export HTMLMediaElement from './HTMLMediaElement'
export HTMLAudioElement from './HTMLAudioElement'
export HTMLVideoElement from './HTMLVideoElement'
export WebGLRenderingContext from './WebGLRenderingContext'
export { TouchEvent, MouseEvent, DeviceMotionEvent } from './EventIniter/index.js'
export localStorage from './localStorage'
export location from './location'
export * from './WindowProperties'
// 暴露全局的 canvas
GameGlobal.screencanvas = GameGlobal.screencanvas || new Canvas()
const canvas = GameGlobal.screencanvas;
const {
setTimeout,
setInterval,
clearTimeout,
clearInterval,
requestAnimationFrame,
cancelAnimationFrame,
} = GameGlobal;
export { canvas }
export { setTimeout }
export { setInterval }
export { clearTimeout }
export { clearInterval }
export { requestAnimationFrame }
export { cancelAnimationFrame }

View File

@@ -0,0 +1,24 @@
if (cc.Texture2D) {
cc.Texture2D.prototype._checkPackable = function () {
let dynamicAtlas = cc.dynamicAtlasManager;
if (!dynamicAtlas) return;
if (this._isCompressed()) {
this._packable = false;
return;
}
let w = this.width, h = this.height;
if (!this._image ||
w > dynamicAtlas.maxFrameSize || h > dynamicAtlas.maxFrameSize ||
this._getHash() !== dynamicAtlas.Atlas.DEFAULT_HASH) {
this._packable = false;
return;
}
// HACK: Can't tell if it's a Canvas or an Image by instanceof on WeChat.
if (this._image && this._image.getContext) {
this._packable = true;
}
};
}

View File

@@ -0,0 +1,339 @@
/****************************************************************************
Copyright (c) 2018 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
(function () {
if (!(cc && cc.VideoPlayer && cc.VideoPlayer.Impl && __globalAdapter.createVideo)) {
return;
}
const Mat4 = cc.Mat4;
var _worldMat = cc.mat4();
var _cameraMat = cc.mat4();
var _impl = cc.VideoPlayer.Impl;
var _p = cc.VideoPlayer.Impl.prototype;
cc.VideoPlayer.prototype._updateVideoSource = function _updateVideoSource() {
let clip = this._clip;
if (this.resourceType === cc.VideoPlayer.ResourceType.REMOTE) {
this._impl.setURL(this.remoteURL, this._mute || this._volume === 0);
}
else if (clip) {
if (clip._nativeAsset) {
this._impl.setURL(clip._nativeAsset, this._mute || this._volume === 0);
}
else {
// deferred loading video clip
cc.assetManager.postLoadNative(clip, (err) => {
if (err) {
console.error(err.message, err.stack);
return;
}
this._impl.setURL(clip._nativeAsset, this._mute || this._volume === 0);
});
}
}
};
_p._bindEvent = function () {
let video = this._video,
self = this;
if (!video) {
return;
}
video.onPlay(function () {
if (self._video !== video) return;
self._playing = true;
self._dispatchEvent(_impl.EventType.PLAYING);
});
video.onEnded(function () {
if (self._video !== video) return;
self._playing = false;
self._currentTime = self._duration; // ensure currentTime is at the end of duration
self._dispatchEvent(_impl.EventType.COMPLETED);
});
video.onPause(function () {
if (self._video !== video) return;
self._playing = false;
self._dispatchEvent(_impl.EventType.PAUSED);
});
video.onTimeUpdate(function (res) {
self._duration = res.duration;
self._currentTime = res.position;
});
// onStop not supported, implemented in promise returned by video.stop call.
};
_p._unbindEvent = function () {
let video = this._video;
if (!video) {
return;
}
// BUG: video.offPlay(cb) is invalid
video.offPlay();
video.offEnded();
video.offPause();
video.offTimeUpdate();
// offStop not supported
};
_p.setVisible = function (value) {
let video = this._video;
if (!video || this._visible === value) {
return;
}
if (value) {
video.width = this._actualWidth || 0;
}
else {
video.width = 0; // hide video
}
this._visible = value;
};
_p.createDomElementIfNeeded = function () {
if (!__globalAdapter.createVideo) {
cc.warn('VideoPlayer not supported');
return;
}
if (!this._video) {
this._video = __globalAdapter.createVideo();
this._video.showCenterPlayBtn = false;
this._video.controls = false;
this._duration = 0;
this._currentTime = 0;
this._loaded = false;
this.setVisible(false);
this._bindEvent();
}
};
_p.setURL = function (path) {
let video = this._video;
if (!video || video.src === path) {
return;
}
video.stop();
this._unbindEvent();
video.autoplay = true; // HACK: to implement onCanplay callback
video.src = path;
video.muted = true;
let self = this;
this._loaded = false;
function loadedCallback () {
video.offPlay();
self._bindEvent();
video.stop();
video.muted = false;
self._loaded = true;
self._playing = false;
self._currentTime = 0;
self._dispatchEvent(_impl.EventType.READY_TO_PLAY);
video.autoplay = false;
}
video.onPlay(loadedCallback);
};
_p.getURL = function() {
let video = this._video;
if (!video) {
return '';
}
return video.src;
};
_p.play = function () {
let video = this._video;
if (!video || !this._visible || this._playing) return;
video.play();
};
_p.setStayOnBottom = function (enabled) {};
_p.pause = function () {
let video = this._video;
if (!this._playing || !video) return;
video.pause();
};
_p.resume = function () {
let video = this._video;
if (this._playing || !video) return;
video.play();
};
_p.stop = function () {
let self = this;
let video = this._video;
if (!video || !this._visible) return;
video.stop().then(function (res) {
if (res.errMsg && !res.errMsg.includes('ok')) {
console.error('failed to stop video player');
return;
}
self._currentTime = 0;
self._playing = false;
self._dispatchEvent(_impl.EventType.STOPPED);
});
};
_p.setVolume = function (volume) {
// wx not support setting video volume
};
_p.seekTo = function (time) {
let video = this._video;
if (!video || !this._loaded) return;
video.seek(time);
};
_p.isPlaying = function () {
return this._playing;
};
_p.duration = function () {
return this._duration;
};
_p.currentTime = function () {
return this._currentTime;
};
_p.setKeepAspectRatioEnabled = function (isEnabled) {
console.warn('On wechat game videoPlayer is always keep the aspect ratio');
};
_p.isKeepAspectRatioEnabled = function () {
return true;
};
_p.isFullScreenEnabled = function () {
return this._fullScreenEnabled;
};
_p.setFullScreenEnabled = function (enable) {
let video = this._video;
if (!video || this._fullScreenEnabled === enable) {
return;
}
if (enable) {
video.requestFullScreen();
}
else {
video.exitFullScreen();
}
this._fullScreenEnabled = enable;
};
_p.enable = function () {
this.setVisible(true);
};
_p.disable = function () {
if (this._playing) {
this._video.pause();
}
this.setVisible(false);
};
_p.destroy = function () {
this.disable();
this._unbindEvent();
if (this._video) {
this._video.destroy();
this._video = undefined;
}
};
_p.updateMatrix = function (node) {
if (!this._video || !this._visible) return;
let camera = cc.Camera.findCamera(node);
if (!camera) {
return;
}
node.getWorldMatrix(_worldMat);
if (this._m00 === _worldMat.m[0] && this._m01 === _worldMat.m[1] &&
this._m04 === _worldMat.m[4] && this._m05 === _worldMat.m[5] &&
this._m12 === _worldMat.m[12] && this._m13 === _worldMat.m[13] &&
this._w === node._contentSize.width && this._h === node._contentSize.height) {
return;
}
// update matrix cache
this._m00 = _worldMat.m[0];
this._m01 = _worldMat.m[1];
this._m04 = _worldMat.m[4];
this._m05 = _worldMat.m[5];
this._m12 = _worldMat.m[12];
this._m13 = _worldMat.m[13];
this._w = node._contentSize.width;
this._h = node._contentSize.height;
camera.getWorldToScreenMatrix2D(_cameraMat);
Mat4.multiply(_cameraMat, _cameraMat, _worldMat);
let viewScaleX = cc.view._scaleX,
viewScaleY = cc.view._scaleY;
let dpr = cc.view._devicePixelRatio;
viewScaleX /= dpr;
viewScaleY /= dpr;
let finalScaleX = _cameraMat.m[0] * viewScaleX,
finalScaleY = _cameraMat.m[5] * viewScaleY;
let finalWidth = this._w * finalScaleX,
finalHeight = this._h * finalScaleY;
let appx = finalWidth * node._anchorPoint.x;
let appy = finalHeight * node._anchorPoint.y;
let viewport = cc.view._viewportRect;
let offsetX = viewport.x / dpr,
offsetY = viewport.y / dpr;
let tx = _cameraMat.m[12] * viewScaleX - appx + offsetX,
ty = _cameraMat.m[13] * viewScaleY - appy + offsetY;
var height = cc.view.getFrameSize().height;
this._video.x = tx;
this._video.y = height - finalHeight - ty;
this._actualWidth = this._video.width = finalWidth;
this._video.height = finalHeight;
};
})();

View File

@@ -0,0 +1,3 @@
require('./VideoPlayer');
require('./pc-adapter');
require('./Texture2D');

View File

@@ -0,0 +1,170 @@
const env = wx.getSystemInfoSync();
const inputMgr = cc.internal.inputManager;
const eventMgr = cc.internal.eventManager;
const EventKeyboard = cc.Event.EventKeyboard;
const EventMouse = cc.Event.EventMouse;
// map from CCMacro
const key2keyCode = {
backspace: 8,
tab: 9,
enter: 13,
shift: 16,
control: 17,
alt: 18,
pause: 19,
capslock: 20,
escape: 27,
' ': 32,
pageup: 33,
pagedown: 34,
end: 35,
home: 36,
arrowleft: 37,
arrowup: 38,
arrowright: 39,
arrowdown: 40,
insert: 45,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
'*': 106,
'+': 107,
'-': 109,
'/': 111,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
numlock: 144,
scrolllock: 145,
';': 186,
'=': 187,
',': 188,
'.': 190,
'`': 192,
'[': 219,
'\\': 220,
']': 221,
'\'': 222,
};
const code2KeyCode = {
Delete: 46,
Digit0: 48,
Digit1: 49,
Digit2: 50,
Digit3: 51,
Digit4: 52,
Digit5: 53,
Digit6: 54,
Digit7: 55,
Digit8: 56,
Digit9: 57,
Numpad0: 96,
Numpad1: 97,
Numpad2: 98,
Numpad3: 99,
Numpad4: 100,
Numpad5: 101,
Numpad6: 102,
Numpad7: 103,
Numpad8: 104,
Numpad9: 105,
NumpadDecimal: 110,
};
function getKeyCode (res) {
let key = res.key.toLowerCase(), code = res.code;
// distinguish different numLock states
if (/^\d$/.test(key) || key === 'delete') {
return code2KeyCode[code];
}
return key2keyCode[key] || 0;
}
function adaptKeyboadEvent () {
wx.onKeyDown(res => eventMgr.dispatchEvent(new EventKeyboard(getKeyCode(res), true)));
wx.onKeyUp(res => eventMgr.dispatchEvent(new EventKeyboard(getKeyCode(res), false)));
}
function adaptMouseEvent () {
let canvasRect = {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight,
};
function registerMouseEvent (funcName, engineEventType, handler) {
wx[funcName](res => {
let mouseEvent = inputMgr.getMouseEvent(res, canvasRect, engineEventType);
mouseEvent.setButton(res.button || 0);
handler(res, mouseEvent);
eventMgr.dispatchEvent(mouseEvent);
});
}
registerMouseEvent('onMouseDown', EventMouse.DOWN, function (res, mouseEvent) {
inputMgr._mousePressed = true;
inputMgr.handleTouchesBegin([inputMgr.getTouchByXY(res.x, res.y, canvasRect)]);
});
registerMouseEvent('onMouseUp', EventMouse.UP, function (res, mouseEvent) {
inputMgr._mousePressed = false;
inputMgr.handleTouchesEnd([inputMgr.getTouchByXY(res.x, res.y, canvasRect)]);
});
registerMouseEvent('onMouseMove', EventMouse.MOVE, function (res, mouseEvent) {
inputMgr.handleTouchesMove([inputMgr.getTouchByXY(res.x, res.y, canvasRect)]);
if (!inputMgr._mousePressed) {
mouseEvent.setButton(null);
}
});
registerMouseEvent('onWheel', EventMouse.SCROLL, function (res, mouseEvent) {
mouseEvent.setScrollData(0, -res.deltaY);
});
}
(function () {
// TODO: add mac
if (__globalAdapter.isSubContext || env.platform !== 'windows') {
return;
}
inputMgr.registerSystemEvent = function () {
if (this._isRegisterEvent) {
return;
}
this._glView = cc.view;
adaptKeyboadEvent();
adaptMouseEvent();
this._isRegisterEvent = true;
};
})();

View File

@@ -0,0 +1,60 @@
// Display error on the screen
function onErrorMessageHandler (info) {
// display once in any case
wx.offError && wx.offError(onErrorMessageHandler);
var allowTrigger = Math.random() < 0.01;
if (__globalAdapter.isSubContext || !allowTrigger) {
return;
}
var env = wx.getSystemInfoSync();
if (!env) {
return;
}
if (!cc.Canvas.instance) {
return;
}
var root = cc.Canvas.instance.node
if (!root) {
return;
}
var padding = 60;
var node = new cc.Node();
node.color = cc.Color.BLACK;
var label = node.addComponent(cc.Label);
node.height = root.height - padding;
node.width = root.width - padding;
label.overflow = cc.Label.Overflow.SHRINK;
label.horizontalAlign = cc.Label.HorizontalAlign.LEFT;
label.verticalAlign = cc.Label.VerticalAlign.TOP;
label.fontSize = 24;
label.string = '出错了请截屏发送给游戏开发者Please send this screenshot to the game developer\n' +
'Platform: WeChat ' + env.version + '\n' +
'Engine: Cocos Creator v' + window.CocosEngine + '\n' +
'Device: ' + env.brand + ' ' + env.model + ' System: ' + env.system + '\n' +
'Error:\n' +
info.message;
if (cc.LabelOutline) {
node.addComponent(cc.LabelOutline).color = cc.Color.WHITE;
}
node.once('touchend', function () {
node.destroy();
setTimeout(function () {
cc.director.resume();
}, 1000);
});
node.parent = root;
cc.director.pause();
}
wx.onError && wx.onError(onErrorMessageHandler);

View File

@@ -0,0 +1,266 @@
/****************************************************************************
Copyright (c) 2017-2019 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of fsUtils software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in fsUtils License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
var fs = wx.getFileSystemManager ? wx.getFileSystemManager() : null;
var outOfStorageRegExp = /the maximum size of the file storage/;
var fsUtils = {
fs,
isOutOfStorage (errMsg) {
return outOfStorageRegExp.test(errMsg);
},
getUserDataPath () {
return wx.env.USER_DATA_PATH;
},
checkFsValid () {
if (!fs) {
console.warn('can not get the file system!');
return false;
}
return true;
},
deleteFile (filePath, onComplete) {
fs.unlink({
filePath: filePath,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Delete file failed: path: ${filePath} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg));
}
});
},
downloadFile (remoteUrl, filePath, header, onProgress, onComplete) {
var options = {
url: remoteUrl,
success: function (res) {
if (res.statusCode === 200) {
onComplete && onComplete(null, res.tempFilePath || res.filePath);
}
else {
if (res.filePath) {
fsUtils.deleteFile(res.filePath);
}
console.warn(`Download file failed: path: ${remoteUrl} message: ${res.statusCode}`);
onComplete && onComplete(new Error(res.statusCode), null);
}
},
fail: function (res) {
console.warn(`Download file failed: path: ${remoteUrl} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg), null);
}
}
if (filePath) options.filePath = filePath;
if (header) options.header = header;
var task = wx.downloadFile(options);
onProgress && task.onProgressUpdate(onProgress);
},
saveFile (srcPath, destPath, onComplete) {
wx.saveFile({
tempFilePath: srcPath,
filePath: destPath,
success: function (res) {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Save file failed: path: ${srcPath} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg));
}
});
},
copyFile (srcPath, destPath, onComplete) {
fs.copyFile({
srcPath: srcPath,
destPath: destPath,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Copy file failed: path: ${srcPath} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg));
}
});
},
writeFile (path, data, encoding, onComplete) {
fs.writeFile({
filePath: path,
encoding: encoding,
data: data,
success: function () {
onComplete && onComplete(null);
},
fail: function (res) {
console.warn(`Write file failed: path: ${path} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg));
}
});
},
writeFileSync (path, data, encoding) {
try {
fs.writeFileSync(path, data, encoding);
return null;
}
catch (e) {
console.warn(`Write file failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
readFile (filePath, encoding, onComplete) {
fs.readFile({
filePath: filePath,
encoding: encoding,
success: function (res) {
onComplete && onComplete(null, res.data);
},
fail: function (res) {
console.warn(`Read file failed: path: ${filePath} message: ${res.errMsg}`);
onComplete && onComplete (new Error(res.errMsg), null);
}
});
},
readDir (filePath, onComplete) {
fs.readdir({
dirPath: filePath,
success: function (res) {
onComplete && onComplete(null, res.files);
},
fail: function (res) {
console.warn(`Read directory failed: path: ${filePath} message: ${res.errMsg}`);
onComplete && onComplete(new Error(res.errMsg), null);
}
});
},
readText (filePath, onComplete) {
fsUtils.readFile(filePath, 'utf8', onComplete);
},
readArrayBuffer (filePath, onComplete) {
fsUtils.readFile(filePath, '', onComplete);
},
readJson (filePath, onComplete) {
fsUtils.readFile(filePath, 'utf8', function (err, text) {
var out = null;
if (!err) {
try {
out = JSON.parse(text);
}
catch (e) {
console.warn(`Read json failed: path: ${filePath} message: ${e.message}`);
err = new Error(e.message);
}
}
onComplete && onComplete(err, out);
});
},
readJsonSync (path) {
try {
var str = fs.readFileSync(path, 'utf8');
return JSON.parse(str);
}
catch (e) {
console.warn(`Read json failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
makeDirSync (path, recursive) {
try {
fs.mkdirSync(path, recursive);
return null;
}
catch (e) {
console.warn(`Make directory failed: path: ${path} message: ${e.message}`);
return new Error(e.message);
}
},
rmdirSync (dirPath, recursive) {
try {
fs.rmdirSync(dirPath, recursive);
}
catch (e) {
console.warn(`rm directory failed: path: ${dirPath} message: ${e.message}`);
return new Error(e.message);
}
},
exists (filePath, onComplete) {
fs.access({
path: filePath,
success: function () {
onComplete && onComplete(true);
},
fail: function () {
onComplete && onComplete(false);
}
});
},
loadSubpackage (name, onProgress, onComplete) {
var task = wx.loadSubpackage({
name: name,
success: function () {
onComplete && onComplete();
},
fail: function (res) {
console.warn(`Load Subpackage failed: path: ${name} message: ${res.errMsg}`);
onComplete && onComplete(new Error(`Failed to load subpackage ${name}: ${res.errMsg}`));
}
});
onProgress && task.onProgressUpdate(onProgress);
return task;
},
unzip (zipFilePath, targetPath, onComplete) {
fs.unzip({
zipFilePath,
targetPath,
success () {
onComplete && onComplete(null);
},
fail (res) {
console.warn(`unzip failed: path: ${zipFilePath} message: ${res.errMsg}`);
onComplete && onComplete(new Error('unzip failed: ' + res.errMsg));
},
})
},
};
window.fsUtils = module.exports = fsUtils;

View File

@@ -0,0 +1,100 @@
// Ensure recieving message from main context before engine being inited
let isEngineReady = false;
const game = cc.game;
game.once(game.EVENT_ENGINE_INITED, function () {
isEngineReady = true;
});
var viewportInMain = {
x: 0,
y: 0,
width: 0,
height: 0
};
// Touch conversion
cc.view.convertToLocationInView = function (tx, ty, relatedPos, out) {
var result = out || cc.v2();
var x = this._devicePixelRatio * (tx - relatedPos.left);
var y = this._devicePixelRatio * (relatedPos.top + relatedPos.height - ty);
// Move to real viewport area
x = (x - viewportInMain.x) * this._viewportRect.width / viewportInMain.width;
y = (y - viewportInMain.y) * this._viewportRect.height / viewportInMain.height;
if (this._isRotated) {
result.x = this._viewportRect.width - y;
result.y = x;
}
else {
result.x = x;
result.y = y;
}
return result;
};
// In sub context, run main loop after subContextView component get enabled.
game._prepareFinished = function (cb) {
this._prepared = true;
// Init engine
this._initEngine();
cc.assetManager.builtins.init(() => {
// Log engine version
console.log('Cocos Creator v' + cc.ENGINE_VERSION);
this._setAnimFrame();
this.emit(this.EVENT_GAME_INITED);
if (cb) cb();
});
};
wx.onMessage(function (data) {
if (data.fromEngine) {
if (data.event === 'boot') {
game._banRunningMainLoop = false;
if (game._firstSceneLaunched) {
game._runMainLoop();
}
}
else if (data.event === 'viewport') {
viewportInMain.x = data.x;
viewportInMain.y = data.y;
viewportInMain.width = data.width;
viewportInMain.height = data.height;
}
else if (data.event === 'resize') {
window.dispatchEvent({type: 'resize'});
}
else if (isEngineReady) {
if (data.event === 'mainLoop') {
if (data.value) {
game.resume();
}
else {
game.pause();
}
}
else if (data.event === 'frameRate') {
game.setFrameRate(data.value);
}
else if (data.event === 'step') {
game.step();
}
}
}
});
// Canvas component adaptation
cc.Canvas.prototype.update = function () {
if (this._width !== game.canvas.width || this._height !== game.canvas.height) {
this.applySettings();
}
};
let originalApplySettings = cc.Canvas.prototype.applySettings;
cc.Canvas.prototype.applySettings = function () {
originalApplySettings.call(this);
this._width = game.canvas.width;
this._height = game.canvas.height;
};

View File

@@ -0,0 +1,58 @@
const adapter = window.__globalAdapter;
const env = wx.getSystemInfoSync();
let adaptSysFunc = adapter.adaptSys;
Object.assign(adapter, {
// Extend adaptSys interface
adaptSys (sys) {
adaptSysFunc.call(this, sys);
// TODO: add mac platform
if (env.platform === 'windows') {
sys.isMobile = false;
sys.os = sys.OS_WINDOWS;
}
else if (adapter.isDevTool) {
let system = env.system.toLowerCase();
if (system.indexOf('android') > -1) {
sys.os = sys.OS_ANDROID;
}
else if (system.indexOf('ios') > -1) {
sys.os = sys.OS_IOS;
}
}
// wechatgame subdomain
if (!wx.getOpenDataContext) {
sys.platform = sys.WECHAT_GAME_SUB;
}
else {
sys.platform = sys.WECHAT_GAME;
}
// sys.glExtension = function (name) {
// if (name === 'OES_texture_float') {
// return false;
// }
// return !!cc.renderer.device.ext(name);
// };
// move to common if other platforms support
sys.getSafeAreaRect = function () {
let view = cc.view;
let safeArea = adapter.getSafeArea();
let screenSize = view.getFrameSize(); // Get leftBottom and rightTop point in UI coordinates
let leftBottom = new cc.Vec2(safeArea.left, safeArea.bottom);
let rightTop = new cc.Vec2(safeArea.right, safeArea.top); // Returns the real location in view.
let relatedPos = {
left: 0,
top: 0,
width: screenSize.width,
height: screenSize.height
};
view.convertToLocationInView(leftBottom.x, leftBottom.y, relatedPos, leftBottom);
view.convertToLocationInView(rightTop.x, rightTop.y, relatedPos, rightTop); // convert view point to design resolution size
view._convertPointWithScale(leftBottom);
view._convertPointWithScale(rightTop);
return cc.rect(leftBottom.x, leftBottom.y, rightTop.x - leftBottom.x, rightTop.y - leftBottom.y);
};
},
});

View File

@@ -0,0 +1,163 @@
const utils = require('../../../common/utils');
if (window.__globalAdapter) {
let globalAdapter = window.__globalAdapter;
// SystemInfo
let systemInfo;
let systemInfoCached = false;
function refreshSystemInfo(delay){
systemInfo = wx.getSystemInfoSync();
// refresh systemInfo, some seconds later.
setTimeout(function () {
systemInfo = wx.getSystemInfoSync();
systemInfoCached = true
}, delay || 5000);
}
refreshSystemInfo();
// NOTE: size and orientation info is wrong at the init phase, especially on iOS device
function isLandscape () {
return systemInfo.deviceOrientation ? (systemInfo.deviceOrientation === "landscape"): (systemInfo.screenWidth > systemInfo.screenHeight);
}
globalAdapter.isSubContext = (wx.getOpenDataContext === undefined);
globalAdapter.isDevTool = (systemInfo.platform === 'devtools');
utils.cloneMethod(globalAdapter, wx, 'getSystemInfoSync');
// TouchEvent
utils.cloneMethod(globalAdapter, wx, 'onTouchStart');
utils.cloneMethod(globalAdapter, wx, 'onTouchMove');
utils.cloneMethod(globalAdapter, wx, 'onTouchEnd');
utils.cloneMethod(globalAdapter, wx, 'onTouchCancel');
// Audio
utils.cloneMethod(globalAdapter, wx, 'createInnerAudioContext');
// AudioInterruption Evnet
utils.cloneMethod(globalAdapter, wx, 'onAudioInterruptionEnd');
utils.cloneMethod(globalAdapter, wx, 'onAudioInterruptionBegin');
// Video
utils.cloneMethod(globalAdapter, wx, 'createVideo');
// FrameRate
utils.cloneMethod(globalAdapter, wx, 'setPreferredFramesPerSecond');
// Keyboard
utils.cloneMethod(globalAdapter, wx, 'showKeyboard');
utils.cloneMethod(globalAdapter, wx, 'hideKeyboard');
utils.cloneMethod(globalAdapter, wx, 'updateKeyboard');
utils.cloneMethod(globalAdapter, wx, 'onKeyboardInput');
utils.cloneMethod(globalAdapter, wx, 'onKeyboardConfirm');
utils.cloneMethod(globalAdapter, wx, 'onKeyboardComplete');
utils.cloneMethod(globalAdapter, wx, 'offKeyboardInput');
utils.cloneMethod(globalAdapter, wx, 'offKeyboardConfirm');
utils.cloneMethod(globalAdapter, wx, 'offKeyboardComplete');
// Message
utils.cloneMethod(globalAdapter, wx, 'getOpenDataContext');
utils.cloneMethod(globalAdapter, wx, 'onMessage');
// SharedCanvas
utils.cloneMethod(globalAdapter, wx, 'getSharedCanvas');
// Font
utils.cloneMethod(globalAdapter, wx, 'loadFont');
// hide show Event
utils.cloneMethod(globalAdapter, wx, 'onShow');
utils.cloneMethod(globalAdapter, wx, 'onHide');
// onError
utils.cloneMethod(globalAdapter, wx, 'onError');
// offError
utils.cloneMethod(globalAdapter, wx, 'offError');
// Accelerometer
let isAccelerometerInit = false;
let deviceOrientation = 1;
if (wx.onDeviceOrientationChange) {
wx.onDeviceOrientationChange(function (res) {
refreshSystemInfo();
if (res.value === 'landscape') {
deviceOrientation = 1;
}
else if (res.value === 'landscapeReverse') {
deviceOrientation = -1;
}
});
}
if (wx.onWindowResize) {
wx.onWindowResize(function () {
refreshSystemInfo();
window.dispatchEvent('resize');
});
}
Object.assign(globalAdapter, {
startAccelerometer (cb) {
if (!isAccelerometerInit) {
isAccelerometerInit = true;
wx.onAccelerometerChange && wx.onAccelerometerChange(function (res) {
let resClone = {};
let x = res.x;
let y = res.y;
if (isLandscape()) {
let tmp = x;
x = -y;
y = tmp;
}
resClone.x = x * deviceOrientation;
resClone.y = y * deviceOrientation;
resClone.z = res.z;
cb && cb(resClone);
});
}
else {
wx.startAccelerometer && wx.startAccelerometer({
fail (err) {
console.error('start accelerometer failed', err);
},
// success () {},
// complete () {},
});
}
},
stopAccelerometer () {
wx.stopAccelerometer && wx.stopAccelerometer({
fail (err) {
console.error('stop accelerometer failed', err);
},
// success () {},
// complete () {},
});
},
});
// safeArea
// origin point on the top-left corner
globalAdapter.getSafeArea = function () {
systemInfo = systemInfoCached ? systemInfo : wx.getSystemInfoSync();
let windowWidth = systemInfo.windowWidth;
let windowHeight = systemInfo.windowHeight;
let { top, left, bottom, right, width, height } = systemInfo.safeArea;
// HACK: on iOS device, the orientation should mannually rotate
if (systemInfo.platform === 'ios' && !globalAdapter.isDevTool && isLandscape()) {
let tmpTop = top, tmpLeft = left, tmpBottom = bottom, tmpRight = right, tmpWidth = width, tmpHeight = height;
let bottomHeight = windowWidth - tmpBottom;
top = windowHeight - tmpRight;
left = tmpTop;
bottom = windowHeight - tmpLeft - bottomHeight;
right = tmpBottom;
height = tmpWidth - bottomHeight;
width = tmpHeight;
}
return { top, left, bottom, right, width, height };
}
}