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

Commit a4434c5

Browse files
authored
Merge pull request #23 from jesstelford/debug-render
Debug render
2 parents e0c5893 + e205b84 commit a4434c5

File tree

4 files changed

+198
-5
lines changed

4 files changed

+198
-5
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/stage.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export default class Stage extends Component {
2222
static childContextTypes = {
2323
loop: PropTypes.object,
2424
scale: PropTypes.number,
25+
renderWidth: PropTypes.number,
26+
renderHeight: PropTypes.number,
2527
};
2628

2729
setDimensions = () => {
@@ -53,8 +55,11 @@ export default class Stage extends Component {
5355
}
5456

5557
getChildContext() {
58+
const { scale, width, height } = this.getScale();
5659
return {
57-
scale: this.getScale().scale,
60+
scale: scale,
61+
renderWidth: width,
62+
renderHeight: height,
5863
loop: this.context.loop,
5964
};
6065
}

src/components/world.js

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

4-
import Matter, {Engine, Events} from 'matter-js';
4+
import Matter, { Render, Engine, Events } from 'matter-js';
55

66
export default class World extends Component {
77
static propTypes = {
@@ -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

@@ -46,6 +55,94 @@ export default class World extends Component {
4655
this.lastTime = currTime;
4756
};
4857

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

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

70198
componentDidMount() {
71199
this.loopID = this.context.loop.subscribe(this.loop);
72-
this.props.onInit(this.engine);
200+
this.onInit(this.engine);
73201
Events.on(this.engine, 'afterUpdate', this.props.onUpdate);
74202
Events.on(this.engine, 'collisionStart', this.props.onCollision);
75203
}
@@ -78,6 +206,7 @@ export default class World extends Component {
78206
this.context.loop.unsubscribe(this.loopID);
79207
Events.off(this.engine, 'afterUpdate', this.props.onUpdate);
80208
Events.off(this.engine, 'collisionStart', this.props.onCollision);
209+
this.stopDebugRendering();
81210
}
82211

83212
getChildContext() {
@@ -95,9 +224,26 @@ export default class World extends Component {
95224
width: '100%',
96225
};
97226

227+
const { renderWidth, renderHeight, scale } = this.context;
228+
229+
let debugRenderTarget = false;
230+
231+
if (this.props.debug) {
232+
debugRenderTarget = (
233+
<canvas
234+
key="debug-render-target"
235+
style={{position: 'relative'}}
236+
width={renderWidth}
237+
height={renderHeight}
238+
ref={this.getCanvasRef}
239+
/>
240+
);
241+
}
242+
98243
return (
99244
<div style={defaultStyles}>
100245
{this.props.children}
246+
{debugRenderTarget}
101247
</div>
102248
);
103249
}

0 commit comments

Comments
 (0)