so://phie

game-engine

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

blob: 0114159156db7a7956ae4a0528b72e6cb3f639b9 (3514 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 for(const i of this.items) {

73 i.items = [];

74 i.collided = false;

75 }

76 const duplicate = [];

77 for(const a of this.items) {

78 for(const b of this.items) {

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

80 if(a !== b) {

81 let collided = false;

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

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

84 }

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

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

87 }

88 else {

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

90 }

91 if(!a.collided) {

92 a.collided = collided;

93 }

94 if(!b.collided) {

95 b.collided = collided;

96 }

97 if(collided) {

98 if(!a.items.includes(b)) {

99 a.items.push(b);

100 }

101 if(!b.items.includes(a)) {

102 b.items.push(a);

103 }

104 }

105 }

106 }

107 }

108 duplicate.push(a);

109 }

110 }

111 add(item) {

112 this.items.push(item);

113 }

114 remove(item) {

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

116 if(i !== -1) {

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

118 }

119 }

120 [Symbol.iterator]() {

121 return this.items.values();

122 }

123}

124

125export class Surface {

126 constructor(x, y, shape) {

127 this.x = x;

128 this.y = y;

129 this.shape = shape;

130 }

131 update() {

132 // nothing

133 }

134 render(g) {

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

136 g.beginPath();

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

138 g.stroke();

139 }

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

141 g.beginPath();

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

143 g.stroke();

144 }

145 }

146 causeEvent(type, events) {

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

148 for(const event of events) {

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

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

151 }

152 }

153 }

154 }

155}

156

157class Rect {

158 constructor(width, height) {

159 this.width = width;

160 this.height = height;

161 }

162}

163

164class Circle {

165 constructor(radius) {

166 this.radius = radius;

167 }

168}

169