Skip to content
This repository was archived by the owner on Feb 7, 2024. It is now read-only.

Commit 55f2e52

Browse files
committed
Debug renderer for World component
1 parent 4ab0e0e commit 55f2e52

File tree

3 files changed

+192
-4
lines changed

3 files changed

+192
-4
lines changed

demo/game/index.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import Fade from './fade';
1515

1616
import GameStore from './stores/game-store';
1717

18+
const KEY_D = 68;
19+
const KEY_A = 65;
20+
1821
export default class Game extends Component {
1922

2023
static propTypes = {
@@ -49,6 +52,12 @@ export default class Game extends Component {
4952
Matter.World.addBody(engine.world, ground);
5053
Matter.World.addBody(engine.world, leftWall);
5154
Matter.World.addBody(engine.world, rightWall);
55+
56+
Matter.Events.on(engine, 'afterUpdate', this.update);
57+
58+
const unsubscribeFromUpdate = () => {
59+
Matter.Events.off(engine, 'afterUpdate', this.update);
60+
}
5261
}
5362

5463
handleEnterBuilding = (index) => {
@@ -60,12 +69,27 @@ export default class Game extends Component {
6069
}, 500);
6170
}
6271

72+
update = () => {
73+
74+
// On first press of "d", enable debug mode
75+
if (this.keyListener.isDown(KEY_D)) {
76+
if (!this.previousDown) {
77+
this.previousDown = true;
78+
this.setState({debug: !this.state.debug});
79+
}
80+
} else {
81+
this.previousDown = false;
82+
}
83+
}
84+
6385
constructor(props) {
6486
super(props);
6587

6688
this.state = {
6789
fade: true,
90+
debug: false,
6891
};
92+
6993
this.keyListener = new KeyListener();
7094
window.AudioContext = window.AudioContext || window.webkitAudioContext;
7195
window.context = window.context || new AudioContext();
@@ -80,18 +104,25 @@ export default class Game extends Component {
80104
fade: false,
81105
});
82106

107+
this.stageXUIUnsubscribe = GameStore.onStageXChange((stageX) => {
108+
this.forceUpdate();
109+
});
110+
83111
this.keyListener.subscribe([
84112
this.keyListener.LEFT,
85113
this.keyListener.RIGHT,
86114
this.keyListener.UP,
87115
this.keyListener.SPACE,
88-
65,
116+
KEY_A,
117+
KEY_D,
89118
]);
90119
}
91120

92121
componentWillUnmount() {
93122
this.stopMusic();
123+
this.unsubscribeFromUpdate();
94124
this.keyListener.unsubscribe();
125+
this.stageXUIUnsubscribe();
95126
}
96127

97128
render() {
@@ -100,6 +131,13 @@ export default class Game extends Component {
100131
<Stage style={{ background: '#3a9bdc' }}>
101132
<World
102133
onInit={this.physicsInit}
134+
debug={this.state.debug && {
135+
offset: {
136+
x: -GameStore.stageX,
137+
y: 0,
138+
},
139+
background: 'rgba(0, 0, 0, 0.5)',
140+
}}
103141
>
104142
<Level
105143
store={GameStore}

demo/game/stores/game-store.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { observable } from 'mobx';
1+
import { observable, autorun } from 'mobx';
22

33
class GameStore {
44
@observable characterPosition = { x: 0, y: 0 };
@@ -9,6 +9,10 @@ class GameStore {
99
this.characterPosition = position;
1010
}
1111

12+
onStageXChange(callback) {
13+
autorun(() => callback(this.stageX));
14+
}
15+
1216
setStageX(x) {
1317
if (x > 0) {
1418
this.stageX = 0;

src/components/world.js

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { Component, PropTypes } from 'react';
22

3-
import Matter, { Engine, Events } from 'matter-js';
3+
import Matter, { Render, Engine, Events } from 'matter-js';
44

55
export default class World extends Component {
66

@@ -11,6 +11,13 @@ export default class World extends Component {
1111
y: PropTypes.number,
1212
scale: PropTypes.number,
1313
}),
14+
debug: PropTypes.shape({
15+
offset: PropTypes.shape({
16+
x: PropTypes.number,
17+
y: PropTypes.number,
18+
}),
19+
background: PropTypes.string,
20+
}),
1421
onCollision: PropTypes.func,
1522
onInit: PropTypes.func,
1623
onUpdate: PropTypes.func,
@@ -29,6 +36,8 @@ export default class World extends Component {
2936

3037
static contextTypes = {
3138
scale: PropTypes.number,
39+
renderWidth: PropTypes.number,
40+
renderHeight: PropTypes.number,
3241
loop: PropTypes.object,
3342
};
3443

@@ -42,6 +51,94 @@ export default class World extends Component {
4251
this.lastTime = currTime;
4352
};
4453

54+
onInit = (...args) => {
55+
if (this.props.debug) {
56+
this.setupDebugRenderer();
57+
}
58+
59+
this.props.onInit(...args);
60+
};
61+
62+
stopDebugRendering = () => {
63+
if (this._render) {
64+
Render.stop(this._render);
65+
delete this._render;
66+
}
67+
};
68+
69+
getDebugProps = () => {
70+
const debugProps = Object.assign(
71+
{
72+
offset: {},
73+
// transparent background to see sprites, etc, in the world
74+
background: 'rgba(0, 0, 0, 0)',
75+
},
76+
this.props.debug || {}
77+
);
78+
79+
debugProps.offset = Object.assign(
80+
{
81+
x: 0,
82+
y: 0,
83+
},
84+
debugProps.offset
85+
);
86+
87+
return debugProps;
88+
};
89+
90+
setupDebugRenderer = () => {
91+
92+
if (!this._debugRenderElement) {
93+
return;
94+
}
95+
96+
const { renderWidth, renderHeight, scale } = this.context;
97+
const { offset, background } = this.getDebugProps();
98+
99+
const width = renderWidth / scale;
100+
const height = renderHeight / scale;
101+
102+
this._render = Render.create({
103+
canvas: this._debugRenderElement,
104+
// Auto-zoom the canvas to the correct game area
105+
bounds: {
106+
min: {
107+
x: offset.x,
108+
y: offset.y,
109+
},
110+
max: {
111+
x: offset.x + width,
112+
y: offset.y + height,
113+
},
114+
},
115+
options: {
116+
wireframeBackground: background,
117+
width: renderWidth,
118+
height: renderHeight,
119+
},
120+
});
121+
122+
// Setting this as part of `.create` crashes Chrome during a deep clone. :/
123+
// My guess: a circular reference
124+
this._render.engine = this.engine;
125+
126+
Render.run(this._render);
127+
};
128+
129+
getCanvasRef = (element) => {
130+
131+
this._debugRenderElement = element;
132+
133+
if (element) {
134+
if (!this._render) {
135+
this.setupDebugRenderer();
136+
}
137+
} else {
138+
this.stopDebugRendering();
139+
}
140+
};
141+
45142
constructor(props) {
46143
super(props);
47144

@@ -61,11 +158,42 @@ export default class World extends Component {
61158
if (gravity !== this.props.gravity) {
62159
this.engine.world.gravity = gravity;
63160
}
161+
162+
if (!nextProps.debug) {
163+
this.stopDebugRendering();
164+
}
165+
}
166+
167+
componentDidUpdate() {
168+
if (this.props.debug && this._render) {
169+
170+
const { renderWidth, renderHeight, scale } = this.context;
171+
172+
const { offset } = this.getDebugProps();
173+
174+
// When context changes (eg; `scale` due to a window resize),
175+
// re-calculate the world stage
176+
this._render.options.width = renderWidth;
177+
this._render.options.height = renderHeight;
178+
179+
this._render.bounds = {
180+
min: {
181+
x: offset.x,
182+
y: offset.y,
183+
},
184+
max: {
185+
x: offset.x + (renderWidth / scale),
186+
y: offset.y + (renderHeight / scale),
187+
},
188+
};
189+
190+
Render.world(this._render);
191+
}
64192
}
65193

66194
componentDidMount() {
67195
this.loopID = this.context.loop.subscribe(this.loop);
68-
this.props.onInit(this.engine);
196+
this.onInit(this.engine);
69197
Events.on(this.engine, 'afterUpdate', this.props.onUpdate);
70198
Events.on(this.engine, 'collisionStart', this.props.onCollision);
71199
}
@@ -74,6 +202,7 @@ export default class World extends Component {
74202
this.context.loop.unsubscribe(this.loopID);
75203
Events.off(this.engine, 'afterUpdate', this.props.onUpdate);
76204
Events.off(this.engine, 'collisionStart', this.props.onCollision);
205+
this.stopDebugRendering();
77206
}
78207

79208
getChildContext() {
@@ -91,9 +220,26 @@ export default class World extends Component {
91220
width: '100%',
92221
};
93222

223+
const { renderWidth, renderHeight, scale } = this.context;
224+
225+
let debugRenderTarget = false;
226+
227+
if (this.props.debug) {
228+
debugRenderTarget = (
229+
<canvas
230+
key="debug-render-target"
231+
style={{position: 'relative'}}
232+
width={renderWidth}
233+
height={renderHeight}
234+
ref={this.getCanvasRef}
235+
/>
236+
);
237+
}
238+
94239
return (
95240
<div style={defaultStyles}>
96241
{this.props.children}
242+
{debugRenderTarget}
97243
</div>
98244
);
99245
}

0 commit comments

Comments
 (0)