0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

jsdoでmatter その2

Posted at

概要

jsdoでmatterやってみた。
ピンボール見つけた。

写真

image.png

サンプルコード

(() => {
	Matter.use(MatterAttractors);
	const PATHS = {
		DOME: '0 0 0 250 19 250 20 231.9 25.7 196.1 36.9 161.7 53.3 129.5 74.6 100.2 100.2 74.6 129.5 53.3 161.7 36.9 196.1 25.7 231.9 20 268.1 20 303.9 25.7 338.3 36.9 370.5 53.3 399.8 74.6 425.4 100.2 446.7 129.5 463.1 161.7 474.3 196.1 480 231.9 480 250 500 250 500 0 0 0',
		DROP_LEFT: '0 0 20 0 70 100 20 150 0 150 0 0',
		DROP_RIGHT: '50 0 68 0 68 150 50 150 0 100 50 0',
		APRON_LEFT: '0 0 180 120 0 120 0 0',
		APRON_RIGHT: '180 0 180 120 0 120 180 0'
	};
	const COLOR = {
		BACKGROUND: '#212529',
		OUTER: '#495057',
		INNER: '#15aabf',
		BUMPER: '#fab005',
		BUMPER_LIT: '#fff3bf',
		PADDLE: '#e64980',
		PINBALL: '#dee2e6'
	};
	const GRAVITY = 0.75;
	const WIREFRAMES = false;
	const BUMPER_BOUNCE = 1.5;
	const PADDLE_PULL = 0.002;
	const MAX_VELOCITY = 40;
	let $currentScore = $('.current-score span');
	let $highScore = $('.high-score span');
	let currentScore, 
        highScore;
	let engine, 
        world,
        render,
        pinball, 
        stopperGroup;
	let leftPaddle,
        leftUpStopper,
        leftDownStopper,
        isLeftPaddleUp;
	let rightPaddle, 
        rightUpStopper,
        rightDownStopper,
        isRightPaddleUp;

	function load() {
		init();
		createStaticBodies();
		createPaddles();
		createPinball();
		createEvents();
	}
	function init() {
		engine = Matter.Engine.create();
		world = engine.world;
		world.bounds = {
			min: {
                x: 0,
                y: 0
            },
			max: {
                x: 500,
                y: 800
            }
		};
		world.gravity.y = GRAVITY;
     	render = Matter.Render.create({
			element: $('.container')[0],
			engine: engine,
			options: {
				width: world.bounds.max.x,
				height: world.bounds.max.y,
				wireframes: WIREFRAMES,
				background: COLOR.BACKGROUND
			}
		});
		Matter.Render.run(render);
		let runner = Matter.Runner.create();
		Matter.Runner.run(runner, engine);
		stopperGroup = Matter.Body.nextGroup(true);
		currentScore = 0;
		highScore = 0;
		isLeftPaddleUp = false;
		isRightPaddleUp = false;
	}
	function createStaticBodies() {
		Matter.World.add(world, [
			boundary(250, -30, 500, 100),
			boundary(250, 830, 500, 100),
			boundary(-30, 400, 100, 800),
			boundary(530, 400, 100, 800),
			path(239, 86, PATHS.DOME),
			wall(140, 140, 20, 40, COLOR.INNER),
			wall(225, 140, 20, 40, COLOR.INNER),
			wall(310, 140, 20, 40, COLOR.INNER),
			bumper(105, 250),
			bumper(225, 250),
			bumper(345, 250),
			bumper(165, 340),
			bumper(285, 340),
			wall(440, 520, 20, 560, COLOR.OUTER),
			path(25, 360, PATHS.DROP_LEFT),
			path(425, 360, PATHS.DROP_RIGHT),
			wall(120, 510, 20, 120, COLOR.INNER),
			wall(330, 510, 20, 120, COLOR.INNER),
			wall(60, 529, 20, 160, COLOR.INNER),
			wall(390, 529, 20, 160, COLOR.INNER),
			wall(93, 624, 20, 98, COLOR.INNER, -0.96),
			wall(357, 624, 20, 98, COLOR.INNER, 0.96),
			path(79, 740, PATHS.APRON_LEFT),
			path(371, 740, PATHS.APRON_RIGHT),
			reset(225, 50),
			reset(465, 30)
		]);
	}
	function createPaddles() {
		leftUpStopper = stopper(160, 591, 'left', 'up');
		leftDownStopper = stopper(140, 743, 'left', 'down');
		rightUpStopper = stopper(290, 591, 'right', 'up');
		rightDownStopper = stopper(310, 743, 'right', 'down');
		Matter.World.add(world, [leftUpStopper, leftDownStopper, rightUpStopper, rightDownStopper]);
		let paddleGroup = Matter.Body.nextGroup(true);
		let paddleLeft = {};
		paddleLeft.paddle = Matter.Bodies.trapezoid(170, 660, 20, 80, 0.33, {
			label: 'paddleLeft',
			angle: 1.57,
			chamfer: {},
			render: {
				fillStyle: COLOR.PADDLE
			}
		});
		paddleLeft.brick = Matter.Bodies.rectangle(172, 672, 40, 80, {
			angle: 1.62,
			chamfer: {},
			render: {
				visible: false
			}
		});
		paddleLeft.comp = Matter.Body.create({
			label: 'paddleLeftComp',
			parts: [paddleLeft.paddle, paddleLeft.brick]
		});
		paddleLeft.hinge = Matter.Bodies.circle(142, 660, 5, {
			isStatic: true,
			render: {
				visible: false
			}
		});
		Object.values(paddleLeft).forEach((piece) => {
			piece.collisionFilter.group = paddleGroup
		});
		paddleLeft.con = Matter.Constraint.create({
			bodyA: paddleLeft.comp,
			pointA: { 
                x: -29.5,
                y: -8.5
            },
			bodyB: paddleLeft.hinge,
			length: 0,
			stiffness: 0
		});
		Matter.World.add(world, [paddleLeft.comp, paddleLeft.hinge, paddleLeft.con]);
		Matter.Body.rotate(paddleLeft.comp, 0.57, { 
            x: 142,
            y: 660
        });
		let paddleRight = {};
		paddleRight.paddle = Matter.Bodies.trapezoid(280, 660, 20, 80, 0.33, {
			label: 'paddleRight',
			angle: -1.57,
			chamfer: {},
			render: {
				fillStyle: COLOR.PADDLE
			}
		});
		paddleRight.brick = Matter.Bodies.rectangle(278, 672, 40, 80, {
			angle: -1.62,
			chamfer: {},
			render: {
				visible: false
			}
		});
		paddleRight.comp = Matter.Body.create({
			label: 'paddleRightComp',
			parts: [paddleRight.paddle, paddleRight.brick]
		});
		paddleRight.hinge = Matter.Bodies.circle(308, 660, 5, {
			isStatic: true,
			render: {
				visible: false
			}
		});
		Object.values(paddleRight).forEach((piece) => {
			piece.collisionFilter.group = paddleGroup
		});
		paddleRight.con = Matter.Constraint.create({
			bodyA: paddleRight.comp,
			pointA: { 
                x: 29.5,
                y: -8.5
            },
			bodyB: paddleRight.hinge,
			length: 0,
			stiffness: 0
		});
		Matter.World.add(world, [paddleRight.comp, paddleRight.hinge, paddleRight.con]);
		Matter.Body.rotate(paddleRight.comp, -0.57, { 
            x: 308, 
            y: 660
        });
	}
	function createPinball() {
		pinball = Matter.Bodies.circle(0, 0, 14, {
			label: 'pinball',
			collisionFilter: {
				group: stopperGroup
			},
			render: {
				fillStyle: COLOR.PINBALL
			}
		});
		Matter.World.add(world, pinball);
		launchPinball();
	}
	function createEvents() {
		Matter.Events.on(engine, 'collisionStart', function(event) {
			let pairs = event.pairs;
			pairs.forEach(function(pair) {
				if (pair.bodyB.label === 'pinball')
                {
					switch (pair.bodyA.label) 
                    {
					case 'reset':
						launchPinball();
					break;
					case 'bumper':
						pingBumper(pair.bodyA);
					break;
					}
				}
			});
		});
		Matter.Events.on(engine, 'beforeUpdate', function(event) {
			Matter.Body.setVelocity(pinball, {
				x: Math.max(Math.min(pinball.velocity.x, MAX_VELOCITY), -MAX_VELOCITY),
				y: Math.max(Math.min(pinball.velocity.y, MAX_VELOCITY), -MAX_VELOCITY),
			});
			if (pinball.position.x > 450 && pinball.velocity.y > 0) 
            {
				Matter.Body.setVelocity(pinball, {
                    x: 0, 
                    y: -5
                });
			}
		});
		Matter.World.add(world, Matter.MouseConstraint.create(engine, {
			mouse: Matter.Mouse.create(render.canvas),
			constraint: {
				stiffness: 0.2,
				render: {
					visible: false
				}
			}
		}));
		$('body').on('keydown', function(e) {
			if (e.which === 37) 
            {
            	isLeftPaddleUp = true;
			} 
            else if (e.which === 39)
            {
            	isRightPaddleUp = true;
			}
		});
		$('body').on('keyup', function(e) {
			if (e.which === 37)
            {
            	isLeftPaddleUp = false;
			} 
            else if (e.which === 39)
            {
            	isRightPaddleUp = false;
			}
		});
		$('.left-trigger').on('mousedown touchstart', function(e) {
			isLeftPaddleUp = true;
        }).on('mouseup touchend', function(e) {
			isLeftPaddleUp = false;
		});
		$('.right-trigger').on('mousedown touchstart', function(e) {
			isRightPaddleUp = true;
		}).on('mouseup touchend', function(e) {
			isRightPaddleUp = false;
		});
	}
	function launchPinball() {
		updateScore(0);
		Matter.Body.setPosition(pinball, { 
            x: 465,
            y: 765
        });
		Matter.Body.setVelocity(pinball, {
            x: 0,
            y: -25 + rand(-2, 2) 
        });
		Matter.Body.setAngularVelocity(pinball, 0);
	}
	function pingBumper(bumper) {
		updateScore(currentScore + 10);
		bumper.render.fillStyle = COLOR.BUMPER_LIT;
		setTimeout(function() {
			bumper.render.fillStyle = COLOR.BUMPER;
		}, 100);
	}
	function updateScore(newCurrentScore) {
		currentScore = newCurrentScore;
		$currentScore.text(currentScore);
		highScore = Math.max(currentScore, highScore);
		$highScore.text(highScore);
	}
	function rand(min, max) {
		return Math.random() * (max - min) + min;
	}
	function boundary(x, y, width, height) {
		return Matter.Bodies.rectangle(x, y, width, height, {
			isStatic: true,
			render: {
				fillStyle: COLOR.OUTER
			}
		});
	}
	function wall(x, y, width, height, color, angle = 0) {
		return Matter.Bodies.rectangle(x, y, width, height, {
			angle: angle,
			isStatic: true,
			chamfer: { 
                radius: 10 
            },
			render: {
				fillStyle: color
			}
		});
	}
	function path(x, y, path) {
		let vertices = Matter.Vertices.fromPath(path);
		return Matter.Bodies.fromVertices(x, y, vertices, {
			isStatic: true,
			render: {
				fillStyle: COLOR.OUTER,
				strokeStyle: COLOR.OUTER,
				lineWidth: 1
			}
		});
	}
	function bumper(x, y) {
		let bumper = Matter.Bodies.circle(x, y, 25, {
			label: 'bumper',
			isStatic: true,
			render: {
				fillStyle: COLOR.BUMPER
			}
		});
		bumper.restitution = BUMPER_BOUNCE;
		return bumper;
	}
	function stopper(x, y, side, position) {
		let attracteeLabel = (side === 'left') ? 'paddleLeftComp' : 'paddleRightComp';
		return Matter.Bodies.circle(x, y, 40, {
			isStatic: true,
			render: {
				visible: false,
			},
			collisionFilter: {
				group: stopperGroup
			},
			plugin: {
				attractors: [function(a, b) {
					if (b.label === attracteeLabel) 
                    {
						let isPaddleUp = (side === 'left') ? isLeftPaddleUp : isRightPaddleUp;
						let isPullingUp = (position === 'up' && isPaddleUp);
						let isPullingDown = (position === 'down' && !isPaddleUp);
						if (isPullingUp || isPullingDown) 
                        {
							return {
								x: (a.position.x - b.position.x) * PADDLE_PULL,
								y: (a.position.y - b.position.y) * PADDLE_PULL,
							};
						}
					}
				}]
			}
		});
	}
	function reset(x, width) {
		return Matter.Bodies.rectangle(x, 781, width, 2, {
			label: 'reset',
			isStatic: true,
			render: {
				fillStyle: '#fff'
			}
		});
	}
	window.addEventListener('load', load, false);
})();

成果物

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?