1. Use the PixiJS rendering engine

Wechat mini game is a different JavaScript runtime environment from the browser, without BOM and DOM API. However, Pixi.js uses JavaScript in combination with other HTML5 technologies to display media, create animations or manage interactive images. Is dependent on the BOM and DOM apis provided by the browser. So if you want to use pixi.js in wechat games, the engine needs to be transformed. RenderingContext, pixi.js, which automatically detects whether to use WebGL or Canvas to create graphics. Regardless of the engine, most of what ends up being done while the game is running is updating the graphics and playing the sound as the user interacts. The development language of small games is JavaScript, so at the bottom of the engine needs to call the drawing API and audio API through JavaScript. The API that a piece of JavaScript code can call at run time is host-environment dependent. The most commonly used console.log is not even part of the core of the JavaScript language, but is provided by the browser hosting environment. Common hosting environments include browsers, Node.js, and so on. Browsers have BOM and DOM apis, while Node.js does not; Node.js has files and network apis provided by core Node.js modules such as FS and NET, while browsers do not have these modules. For example, the following code that works in a browser will report an error when run in Node.js.

let canvas = document.createElement('canvas')
Copy the code

This is because the node.js hosting environment does not provide the built-in global variable document at all.

ReferenceError: document is not defined
Copy the code


The mini game runs in a different host environment from the browser. Instead of providing BOM and DOM apis, it provides WX apis. With the WX API, developers can call on Native’s capabilities for drawing, audio and video, networking, documentation, and more.



If you want to create a canvas, you need to callwx.createCanvas()

let canvas = wx.createCanvas()
let context = canvas.getContext('2d')
Copy the code

If you want to create an audio object, you need to call wx. CreateInnerAudioContext ()

let audio = wx.createInnerAudioContext()
// The SRC address is for demonstration only. It does not exist
audio.src = 'bgm.mp3'
audio.play()
Copy the code

If you want to get the width and height of the screen, you need to call wx.getSystemInfoSync()

let { screenWidth, screenHeight } = wx.getSystemInfoSync()
Copy the code

But the Pixi.js based rendering engine creates the stage and mounts it to the page in the following way

document.body.appendChild(app.view);
Copy the code

This generates an error because, as mentioned earlier, the mini-game hosting environment does not provide the global variables document and window that are built into the browser. Because the mini-game environment is a different hosting environment than the browser.

ReferenceError: document is not defined
ReferenceError: window is not defined
Copy the code


As a result, almost all pixi.js mini-games cannot be directly transferred to mini-games, because the implementation of pixi.js may more or less use the BOM and DOM API specific to the browser environment. Pixi.js needs to be modified to make calls to the BOM and DOM API instead of the WX API to run in a mini-game environment.



However, we cannot change the pixi.js code, nor can we directly modify the IMPLEMENTATION of the API. There is another adaptation method, which is to add a layer between the rendering engine and the game logic code to simulate the BOM and DOM API adaptation layer, we call Adapter. This mediation layer globally simulates the attributes and methods of the window and Document objects that the engine will access through the WX API, so that the engine doesn’t feel the difference in the environment.



Adapter is user code, not part of the base library. For an introduction to Adapter, see the tutorialAdapter.

2. Adapter Adapter

1. weapp-adapter

The running environment of small games is JavaScriptCore on iOS and V8 on Android. Both of them have no BOM and DOM running environment and no global Document and Window object. So when you try to use the DOM API to create elements like Canvas and Image, you get an error.

const canvas = document.createElement('canvas')
Copy the code

But we can use wx.createCanvas and wx.createImage to encapsulate a document.

const document = {
    createElement: function (tagName) {
        tagName = tagName.toLowerCase()
        if (tagName === 'canvas') {
            return wx.createCanvas()
        }
        else if (tagName === 'image') {
            return wx.createImage()
        }
    }
}
Copy the code

The code can then create the Canvas and Image just as it would create elements in the browser.

const canvas = document.createElement('canvas')
const image = document.createImage('image')
Copy the code

Similarly, if you want to create an Image object as new Image(), add the following code.

function Image () {
    return wx.createImage()
}
Copy the code

These libraries of code that simulates BOM and DOM using the WX API are called Adapters. As the name suggests, this is a layer of adaptation for the browser-based game engine in the small game environment, so that the game engine does not generate errors when calling the DOM API and accessing DOM properties. Adapter is an abstract layer of code, not a specific third-party library for mini-games, and each developer can implement the Adapter for his or her own project. The app provides a complete source code for developers to use and reference. Downloadable p-adapter.zip Appellant p-adapter preevaluates wx.createcanvas () to create an overscreen Canvas and expose it as a global variable Canvas.

require('./weapp-adapter')
var context = canvas.getContext('2d')
context.fillStyle = 'red'
context.fillRect(0.0.100.100)
Copy the code

In addition, the following objects and methods are simulated by THE P-Adapter:

  • document.createElement
  • canvas.addEventListener
  • localStorage
  • Audio
  • Image
  • WebSocket
  • XMLHttpRequest
  • And so on…

It is important to note that the p-Adapter is far from complete in its simulation of the browser environment. It only simulates the attributes and methods that game engines may access, and there is no guarantee that all engines will be able to access games seamlessly through p-Adapter. The app provides a “participatory P-Adapter” directly for developers to use as a reference, allowing them to extend it if needed to accommodate their own game engines.

2. pixi-adapter

Little game base libraries provide only wx. CreateCanvas and wx createImage wx API and setTimeout/setInterval/requestAnimationFrame JS method in common use.

1. Global objects

The Window object is a global object in the browser environment. There is no BOM API in the mini game runtime environment, so there is no Window object. But mini-games provide a global object called GameGlobal, and all globally-defined variables are properties of GameGlobal.

console.log(GameGlobal.setTimeout === setTimeout);
console.log(GameGlobal.requestAnimationFrame === requestAnimationFrame);
Copy the code

The above code execution results are true. Developers can mount their own wrapped classes and functions to GameGlobal as needed.

GameGlobal.render = function(){
    // Specific method implementation
}
render();
Copy the code

2

import { canvas } from './canvas'

/** * Base Element */
export class Element {
  style = { cursor: null }

  appendChild() {}

  removeChild() {}

  addEventListener() {}

  removeEventListener(){}}export const HTMLCanvasElement = canvas.constructor

export const HTMLImageElement = wx.createImage().constructor

export class HTMLVideoElement extends Element {}Copy the code

3. The document structure

import { Canvas } from './canvas'
import Image from './Image'
import { Element } from './element'

const stack = {}

/** * document ADAPTS */
export default {
  body: new Element('body'),

  addEventListener(type, handle) {
    stack[type] = stack[type] || []
    stack[type].push(handle)
  },

  removeEventListener(type, handle) {
    if (stack[type] && stack[type].length) {
      consti = stack[type].indexOf(handle) i ! = = -1 && stack[type].splice(i)
    }
  },

  dispatch(ev) {
    const queue = stack[ev.type]
    queue && queue.forEach(handle= > handle(ev))
  },

  createElement(tag) {
    switch (tag) {
      case 'canvas': {
        return new Canvas()
      }

      case 'img': {
        return new Image()
      }

      default: {
        return new Element()
      }
    }
  }
}
Copy the code

4. Unified entrance

import { noop } from './util'
import Image from './Image'
import { canvas } from './canvas'
import location from './location'
import document from './document'
import WebSocket from './WebSocket'
import navigator from './navigator'
import TouchEvent from './TouchEvent'
import XMLDocument from './XMLDocument'
import localStorage from './localStorage'
import XMLHttpRequest from './XMLHttpRequest'
import { Element, HTMLCanvasElement, HTMLImageElement, HTMLVideoElement } from './element'

const { platform } = wx.getSystemInfoSync()

GameGlobal.canvas = canvas / / global canvas
canvas.addEventListener = document.addEventListener
canvas.removeEventListener = document.removeEventListener

// Cannot be modified when emulator mounts window
if (platform === 'devtools') {
  Object.defineProperties(window, {
    Image: {value: Image},
    Element: {value: Element},
    ontouchstart: {value: noop},
    WebSocket: {value: WebSocket},
    addEventListener: {value: noop},
    TouchEvent: {value: TouchEvent},
    XMLDocument: {value: XMLDocument},
    localStorage: {value: localStorage},
    XMLHttpRequest: {value: XMLHttpRequest},
    HTMLVideoElement: {value: HTMLVideoElement},
    HTMLImageElement: {value: HTMLImageElement},
    HTMLCanvasElement: {value: HTMLCanvasElement},
  })
  / / mount the document
  for (const key in document) {
    const desc = Object.getOwnPropertyDescriptor(window.document, key)
    if(! desc || desc.configurable) {Object.defineProperty(window.document, key, {value: document[key]})
    }
  }
} else {
  GameGlobal.Image = Image
  GameGlobal.window = GameGlobal
  GameGlobal.ontouchstart = noop
  GameGlobal.document = documentGameGlobal.location = location GameGlobal.WebSocket = WebSocket GameGlobal.navigator = navigator GameGlobal.TouchEvent =  TouchEvent GameGlobal.addEventListener = noop GameGlobal.XMLDocument = XMLDocument GameGlobal.removeEventListener = noop GameGlobal.localStorage =localStorage
  GameGlobal.XMLHttpRequest = XMLHttpRequest
  GameGlobal.HTMLImageElement = HTMLImageElement
  GameGlobal.HTMLVideoElement = HTMLVideoElement
  GameGlobal.HTMLCanvasElement = HTMLCanvasElement
  GameGlobal.WebGLRenderingContext = GameGlobal.WebGLRenderingContext || {}
}
Copy the code

The recommended method is to introduce a common Adapter and try to run it, and then solve the problems one by one.