Adding a spine to a container works fine, but if you create another container the webGL crash with error:
WebGL: INVALID_OPERATION: bufferSubData: no buffer
Using this code:
https://labs.phaser.io/edit.html?src=src\spine\spine%20inside%20container.js
If you add a new container BEFORE the container that contains the spines (just add one line var newCont = this.add.container(400, 300)) the program will crash:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d66',
scene: {
preload: preload,
create: create,
update: update,
pack: {
files: [
{ type: 'scenePlugin', key: 'SpinePlugin', url: 'plugins/SpinePluginDebug.js', sceneKey: 'spine' }
]
}
}
};
var controls;
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('logo', 'assets/sprites/phaser.png');
this.load.setPath('assets/animations/spine/webgl/');
this.load.spine('boy', 'spineboy-ess.json', 'spineboy.atlas', true);
this.load.spine('coin', 'coin-pro.json', 'coin.atlas');
}
function create ()
{
this.add.image(0, 0, 'logo').setOrigin(0);
var spineBoy = this.add.spine(0, 0, 'boy', 'walk', true).setScale(0.5);
var coin = this.add.spine(0, 0, 'coin', 'rotate', true).setScale(0.3);
var newCont = this.add.container(400, 300);
var container = this.add.container(400, 300, [ spineBoy, coin ]);
this.tweens.add({
targets: container,
angle: 360,
duration: 6000,
repeat: -1
});
var cursors = this.input.keyboard.createCursorKeys();
var controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
}
function update (time, delta)
{
controls.update(delta);
}
If you add a container AFTER the container that contains the spines (just add one line var newCont = this.add.container(400, 300)) the program will also stop working well with no error message:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 800,
height: 600,
backgroundColor: '#2d2d66',
scene: {
preload: preload,
create: create,
update: update,
pack: {
files: [
{ type: 'scenePlugin', key: 'SpinePlugin', url: 'plugins/SpinePluginDebug.js', sceneKey: 'spine' }
]
}
}
};
var controls;
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('logo', 'assets/sprites/phaser.png');
this.load.setPath('assets/animations/spine/webgl/');
this.load.spine('boy', 'spineboy-ess.json', 'spineboy.atlas', true);
this.load.spine('coin', 'coin-pro.json', 'coin.atlas');
}
function create ()
{
this.add.image(0, 0, 'logo').setOrigin(0);
var spineBoy = this.add.spine(0, 0, 'boy', 'walk', true).setScale(0.5);
var coin = this.add.spine(0, 0, 'coin', 'rotate', true).setScale(0.3);
var container = this.add.container(400, 300, [ spineBoy, coin ]);
var newCont = this.add.container(400, 300);
this.tweens.add({
targets: container,
angle: 360,
duration: 6000,
repeat: -1
});
var cursors = this.input.keyboard.createCursorKeys();
var controlConfig = {
camera: this.cameras.main,
left: cursors.left,
right: cursors.right,
up: cursors.up,
down: cursors.down,
acceleration: 0.06,
drag: 0.0005,
maxSpeed: 1.0
};
controls = new Phaser.Cameras.Controls.SmoothedKeyControl(controlConfig);
}
function update (time, delta)
{
controls.update(delta);
}
I have same problem too!!!
me too
Same here

W/o another spines in a container looks:

Ok, it seems i got the source of problem.
Its related on new members of renderer:
WebGLRenderer.newType
WebGLRenderer.nextTypeMatch
It looks like a little bug in a detection algorithm:
// list is a scene.children.list.
nextTypeMatch = (i < childCount - 1) ? (list[i + 1].type === this.currentType) : false;
//child is an item of list. And list is a scene.children.list.
if (child.type!== this.currentType)
{
this.newType = true;
this.currentType = child.type;
}
_So if u add an object to the parentContainer it will remove this object from a display list of the scene, and the order of the render-queue will be messed._
The quick solution is to redefine a render function in ur spine objects like this:
create(){
...
//Code with a problem:
const container = this.add.container(400, 300);
const spineGameObject = this.add.spine(100, 550, 'vine', 'grow', true);
container.add(spineGameObject);
...
//Quick solution:
const originalRenderWebGL = spineGameObject.renderWebGL;
spineGameObject.renderWebGL = function(renderer, ...args) {
if (this.parentContainer) {
renderer.nextTypeMatch = false;
renderer.newType = true;
}
return originalRenderWebGL.call (this, renderer, ...args);
}
}
import SpineGameObject from "phaser/plugins/spine/src/gameobject/SpineGameObject";
class FixedSpine extends SpineGameObject
{
.... //Some redefined methods
renderWebGL (renderer, ...args) {
if (this.parentContainer) {
renderer.nextTypeMatch = false;
renderer.newType = true;
}
return super.renderWebGL(renderer, ...args);
}
}
...//Dont forget to replace original spine class with yours. And reregister your own spine factory.
//You can use my helper
const GOFactory = Phaser.GameObjects.GameObjectFactory;
const GOFactoryProto = GOFactory.prototype;
const register = GOFactory.register;
/**
* @memberOf Phaser.GameObjects.GameObjectFactory
* @method register
* @param {string} type
* @param {function} creator
* @param overwrite
* @param configurable
*/
GOFactory.register = function (type = "type", creator = noop, overwrite = false, configurable = true) {
if (overwrite) {
if (!delete GOFactoryProto[type]) {
return;
}
}
register(type, creator);
const factory = GOFactoryProto[type];
if (delete GOFactoryProto[type]) {
Object.defineProperty(GOFactoryProto, type, {
value: factory,
configurable,
enumerable: true,
writable: false,
});
}
};
//Then use it like this:
const creator = function (x = 0, y = 0, key = "", animationName = "", loop = false) {
...
let scene = this.scene;
let spine = new FixedSpine(
scene,
scene["spine"], // "spine" is a mapping key for spine plugin.
x,
y,
key,
animationName,
loop,
);
if (!spine.parentContainer) {
this.displayList && this.displayList.add && this.displayList.add(spine);
}
this.updateList && this.updateList.add && this.updateList.add(spine);
return spine;
}
Phaser.GameObjects.GameObjectFactory.register("spine", creator, true, false);
//So after that u can simply use this.add.spine(...) like before with ur own spine class.
Hope its gonna help u. Cya
Ok, it seems i got the source of problem.
Its related on new members of renderer:
WebGLRenderer.newType
WebGLRenderer.nextTypeMatchIt looks like a little bug in a detection algorithm:
// list is a scene.children.list. nextTypeMatch = (i < childCount - 1) ? (list[i + 1].type === this.currentType) : false;//child is an item of list. And list is a scene.children.list. if (child.type!== this.currentType) { this.newType = true; this.currentType = child.type; }_So if u add an object to the parentContainer it will remove this object from a display list of the scene, and the order of the render-queue will be messed._
The quick solution is to redefine a render function in ur spine objects like this:
create(){ ... //Code with a problem: const container = this.add.container(400, 300); const spineGameObject = this.add.spine(100, 550, 'vine', 'grow', true); container.add(spineGameObject); ... //Quick solution: const originalRenderWebGL = spineGameObject.renderWebGL; spineGameObject.renderWebGL = function(renderer, ...args) { if (this.parentContainer) { renderer.nextTypeMatch = false; renderer.newType = true; } return originalRenderWebGL.call (this, renderer, ...args); } }Better solution is to inherit ur own Spine class with fixes:
import SpineGameObject from "phaser/plugins/spine/src/gameobject/SpineGameObject"; class FixedSpine extends SpineGameObject { .... //Some redefined methods renderWebGL (renderer, ...args) { if (this.parentContainer) { renderer.nextTypeMatch = false; renderer.newType = true; } return super.renderWebGL(renderer, ...args); } } ...//Dont forget to replace original spine class with yours. And reregister your own spine factory. //You can use my helper const GOFactory = Phaser.GameObjects.GameObjectFactory; const GOFactoryProto = GOFactory.prototype; const register = GOFactory.register; /** * @memberOf Phaser.GameObjects.GameObjectFactory * @method register * @param {string} type * @param {function} creator * @param overwrite * @param configurable */ GOFactory.register = function (type = "type", creator = noop, overwrite = false, configurable = true) { if (overwrite) { if (!delete GOFactoryProto[type]) { return; } } register(type, creator); const factory = GOFactoryProto[type]; if (delete GOFactoryProto[type]) { Object.defineProperty(GOFactoryProto, type, { value: factory, configurable, enumerable: true, writable: false, }); } }; //Then use it like this: const creator = function (x = 0, y = 0, key = "", animationName = "", loop = false) { ... let scene = this.scene; let spine = new FixedSpine( scene, scene["spine"], // "spine" is a mapping key for spine plugin. x, y, key, animationName, loop, ); if (!spine.parentContainer) { this.displayList && this.displayList.add && this.displayList.add(spine); } this.updateList && this.updateList.add && this.updateList.add(spine); return spine; } Phaser.GameObjects.GameObjectFactory.register("spine", creator, true, false); //So after that u can simply use this.add.spine(...) like before with ur own spine class.Hope its gonna help u. Cya
Hey, thanks for the solution :). Is any way we can solve this directly in Phaser or in the Spine Pluging?
Hey, thanks for the solution :). Is any way we can solve this directly in Phaser or in the Spine Pluging?
Sure.
Remove conditions at the lines:
_(remove only 'if(...)' and keep code in a brackets)_
Thanks for posting a work-around @justsl - be warned, using this approach will break Spine batching, causing a render flush for every container in your game. This may, or may not, matter, depending on how many you have.
Is there any plan to fix this?
Thank you for submitting this issue. We have fixed this and the fix has been pushed to the master branch. It will be part of the next release. If you get time to build and test it for yourself we would appreciate that.
Most helpful comment
Ok, it seems i got the source of problem.
Its related on new members of renderer:
WebGLRenderer.newType
WebGLRenderer.nextTypeMatch
It looks like a little bug in a detection algorithm:
_So if u add an object to the parentContainer it will remove this object from a display list of the scene, and the order of the render-queue will be messed._
The quick solution is to redefine a render function in ur spine objects like this:
Better solution is to inherit ur own Spine class with fixes:
Hope its gonna help u. Cya