so://phie

game-engine

A lightweight JavaScript game engine
git clone git://soophie.de/game-engine

blob: b9b38f37e1db0123504be51a724b2a708be01d0b (3295 bytes)[doc][blame][raw]

1import { scaleCanvas } from './canvas.js';

2import * as Collision from './collision.js';

3

4export class Game {

5 constructor(width, height, { fps } = {}) {

6 this.width = width || 900;

7 this.height = height || 600;

8 this.fps = 1000 / (fps || 60);

9 this.then = Date.now();

10 this.canvas = document.createElement('canvas');

11 scaleCanvas(this.canvas, this.width, this.height);

12 this.g = this.canvas.getContext('2d');

13 this.events = [];

14 }

15 start() {

16 requestAnimationFrame(this.loop.bind(this));

17 this.update();

18 this.render(this.g);

19 }

20 loop() {

21 requestAnimationFrame(this.loop.bind(this));

22 this.now = Date.now();

23 this.elapsed = this.now - this.then;

24 if(this.elapsed > this.fps) {

25 this.then = this.now - (this.elapsed % this.fps);

26 for(const event of this.events) {

27 if(this['on' + event.type]) {

28 this['on' + event.type](event);

29 }

30 }

31 this.update();

32 this.render(this.g);

33 this.events.length = 0;

34 }

35 }

36 update() {

37 // nothing

38 }

39 render() {

40 this.g.clearRect(0, 0, this.width, this.height);

41 }

42 resize(width, height) {

43 this.width = width;

44 this.height = height;

45 scaleCanvas(this.canvas, this.width, this.height);

46 }

47 bindEvent(element, type) {

48 element['on' + type] = event => this.events.push(event);

49 }

50}

51

52export class Group {

53 constructor() {

54 this.items = [];

55 }

56 update() {

57 for(let item of this.items) {

58 item.update();

59 }

60 }

61 render(g) {

62 for(let item of this.items) {

63 item.render(g);

64 }

65 }

66 causeEvent(type, events) {

67 for(let item of this.items) {

68 item.causeEvent(type, events);

69 }

70 }

71 collide() {

72 const duplicate = [];

73 for(const a of this.items) {

74 for(const b of this.items) {

75 if(!duplicate.includes(b)) {

76 if(a !== b) {

77 let collided = false;

78 if(a.shape instanceof Shape.Rect && b.shape instanceof Shape.Rect) {

79 collided = Collision.collideRect(a, b);

80 }

81 else if(a.shape instanceof Shape.Circle && b.shape instanceof Shape.Circle) {

82 collided = Collision.collideCircle(a, b);

83 }

84 else {

85 collided = Collision.collideRectAndCircle(a, b);

86 }

87 if(collided) {

88 if(a.oncollide) {

89 a.oncollide(b);

90 }

91 if(b.oncollide) {

92 b.oncollide(a);

93 }

94 }

95 }

96 }

97 }

98 duplicate.push(a);

99 }

100 }

101 add(item) {

102 this.items.push(item);

103 }

104 remove(item) {

105 const i = this.items.findIndex(n => n === item);

106 if(i !== -1) {

107 this.items.splice(i, 1);

108 }

109 }

110 [Symbol.iterator]() {

111 return this.items.values();

112 }

113}

114

115export class Surface {

116 constructor(x, y, shape) {

117 this.x = x;

118 this.y = y;

119 this.shape = shape;

120 }

121 update() {

122 // nothing

123 }

124 render(g) {

125 if(this.shape instanceof Shape.Rect) {

126 g.beginPath();

127 g.rect(this.x, this.y, this.shape.width, this.shape.height);

128 g.stroke();

129 }

130 if(this.shape instanceof Shape.Circle) {

131 g.beginPath();

132 g.arc(this.x, this.y, this.shape.radius, 2 * Math.PI, 0);

133 g.stroke();

134 }

135 }

136 causeEvent(type, events) {

137 if(this['on' + type]) {

138 for(const event of events) {

139 if(event.type === type) {

140 this['on' + type](event);

141 }

142 }

143 }

144 }

145}

146

147class Rect {

148 constructor(width, height) {

149 this.width = width;

150 this.height = height;

151 }

152}

153

154class Circle {

155 constructor(radius) {

156 this.radius = radius;

157 }

158}

159