OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Matter JS Prevent mouse from dragging a body beyond a limit within the screen and outside the screen?

  • Thread starter Thread starter Worker1432
  • Start date Start date
W

Worker1432

Guest
In the game, the user should be able to toss/throw a ball at static targets to get points.

However, I cannot figure out how to make the ball draggable only in a certain restricted region.

Otherwise, the user can just drag the ball all the way up to the targets instead of having to toss the ball at them. Moreover, the user can also drag the ball offscreen and then the ball disappears permanently unless you reload the screen.

Manipulating the canvas element didn't work. Limiting the ball's velocity to fix the ball disappearing issue also didn't work.

Links on GitHub: https://github.com/mohanamisra/toss-meh/tree/main/src
Demo: https://toss-game-8d371.web.app/
Ball disappears permanently on dragging it offscreen and holding for a second or so.

CODE: The main App

Code:
import React, { useRef, useEffect } from 'react';
import p5 from 'p5';
import Matter from 'matter-js'
import './App.css'
import Ball from "./components/Ball.jsx";
import Boundary from "./components/Boundary.jsx";
import Box from "./components/Box.jsx";
import Scoreboard from "./components/Scoreboard.jsx";
import bg from "./assets/images/background.webp";
import candy from "./assets/images/candy.webp";
import skull from "./assets/images/skull.webp";
import ghost from "./assets/images/ghost.webp";
import zombie from "./assets/images/zombie.webp";
import goblin from "./assets/images/goblin.webp";
import vampire from "./assets/images/vampire.webp";
import scoreboard from "./assets/images/scoreboard.webp";
import '@fontsource-variable/pixelify-sans';


let Engine = Matter.Engine;
let World = Matter.Composite;
let Mouse = Matter.Mouse;
let MouseConstraint = Matter.MouseConstraint;
let Collision = Matter.Collision;
let ground, ball, world, engine, mCon, leftWall, rightWall, scoreBoard;
let boxes = [];
let score = 0;
let timer = 42;
let bgImage, ballImage, scoreboardImage;
let monsterImages = [];
let imgIndex = 0;
let myFont;

function sketch(p) {
    p.preload = async function() {
        bgImage = p.loadImage(bg);
        ballImage = p.loadImage(candy);
        scoreboardImage = p.loadImage(scoreboard);
        monsterImages.push(await p.loadImage(skull));
        monsterImages.push(await p.loadImage(ghost));
        monsterImages.push(await p.loadImage(zombie));
        monsterImages.push(await p.loadImage(goblin));
        monsterImages.push(await p.loadImage(vampire));
    }

    p.setup = function() {
        const canvas = p.createCanvas(p.windowWidth, p.windowHeight);
        engine = Engine.create();
        world = engine.world;
        const canvasForMouse = {...canvas};
        canvasForMouse.elt = <canvas id="defaultCanvas0" className="p5Canvas" width="1648" height="3660"
                                     style="width: 412px; height: 915px;"></canvas>
        console.log(canvas.elt);
        console.log(canvasForMouse.elt);
        const canvasMouse = Mouse.create(canvasForMouse.elt);
        canvasMouse.pixelRatio = p.pixelDensity();
        const options = {
            mouse: canvasMouse,
        }
        mCon = MouseConstraint.create(engine, options);

        ground = new Boundary(p.width/2, p.height-10, p.width, 20, world);
        leftWall = new Boundary(10, p.height/2, 20, p.height, world);
        rightWall = new Boundary(p.width - 10, p.height/2, 20, p.height, world);
        ball = new Ball(p.width/2, p.height - 100, 40, world);
        scoreBoard = new Scoreboard( 10, 10, p.width > 800 ? 400 : p.width - 30, 75, world, scoreboardImage);

        World.add(world, [mCon]);
    }

    setInterval(spawnBoxes, 500);

    function spawnBoxes() {
        if(boxes.length > 7) {
            World.remove(engine.world, boxes[0].body);
            boxes.splice(0, 1);
        }
        let xLoc = p.random(0, p.width);
        let yLoc = p.random(100, p.height/2 + 100);
        let boxWidth, boxHeight;
        let overlapping = false;
        if(imgIndex === 3 || imgIndex === 4) {
            boxWidth = 105;
            boxHeight = 85;
        }
        else {
            boxWidth = 80;
            boxHeight = 85;
        }
        let boxToPush = {
            x: xLoc,
            y: yLoc,
            w: boxWidth,
            h: boxHeight,
            world: world,
            img: monsterImages[imgIndex],
        }

        for(let j = 0; j < boxes.length; j++) {
            let boxToCheckAgainst = boxes[j];
            let distance = p.dist(boxToPush.x, boxToPush.y ,boxToCheckAgainst.x, boxToCheckAgainst.y);
            if(distance < boxToPush.w + boxToCheckAgainst.w - 80
            || distance < boxToPush.h + boxToCheckAgainst.h - 80) {
                overlapping = true;
                break;
            }
        }
        if(!overlapping) {
            boxes.push(new Box(boxToPush));
            imgIndex = (imgIndex + 1) % monsterImages.length;
        }

    }

    p.windowResized = function() {
        p.resizeCanvas(p.windowWidth, p.windowHeight);
    }

    p.draw = function () {
        p.background(bgImage);
        Engine.update(engine);
        rightWall.show(p);
        leftWall.show(p);
        ground.show(p);
        ball.show(p, ballImage);
        if(p.frameCount % 60 === 0 && timer > 0) {
            timer--;
        }
        scoreBoard.show(p, score, timer);

        for(let i = 0; i < boxes.length; i++) {
            boxes[i].show(p);
        }

        if(ball.body.position.y < 0 || ball.body.position.y > p.height + 50 || ball.body.position.x < 0 || ball.body.position.x > p.width + 50) {
            ball.reset(p);
        }

        for(let i = 0; i < boxes.length; i++) {
            if(Collision.collides(ball.body, boxes[i].body)) {
                console.log("HIT ", i);
                World.remove(engine.world, boxes[i].body);
                boxes.splice(i, 1);
                ball.reset(p);
                score += 1;
            }
        }
    }
}

function App() {
    const p5Container = useRef();

    useEffect(() => {
        const p5Instance = new p5(sketch, p5Container.current);

        return() => {
            p5Instance.remove();
        }
    }, []);

    return (
        <>
            <div ref={p5Container} className="container">
            </div>
        </>
    );
}

export default App;

Ball class:

Code:
import Matter from 'matter-js'

export default class Ball {
    constructor(x, y, r, world) {
        this.body = Matter.Bodies.circle(x, y, r);
        Matter.World.add(world, this.body);
        this.r = r;
    }

    show(p, ballImage) {
        const pos = this.body.position;
        const angle = this.body.angle;
        Matter.Body.setSpeed(this.body, Math.min(this.body.speed, 40));
        p.push();
        p.translate(pos.x, pos.y);
        p.rotate(angle);
        p.imageMode(p.CENTER);
        p.image(ballImage, 0, 0, this.r*2, this.r*2);
        p.pop();
    }

    reset(p) {
        Matter.Body.setPosition(this.body, {x: p.width/2, y: p.height - 100});
        Matter.Body.setAngle(this.body, 0);
        Matter.Body.setSpeed(this.body, 0);
        const originalRadius = this.r;
        setTimeout(() => {
            this.r = 0.00001;
            setTimeout(() => {
                this.r = originalRadius;
            }, 300);
        }, 0);
    }
}

How do I resolve this issue?

Thanks in advance!

<p>In the game, the user should be able to toss/throw a ball at static targets to get points.</p>
<p>However, I cannot figure out how to make the ball draggable only in a certain restricted region.</p>
<p>Otherwise, the user can just drag the ball all the way up to the targets instead of having to toss the ball at them. Moreover, the user can also drag the ball offscreen and then the ball disappears permanently unless you reload the screen.</p>
<p>Manipulating the canvas element didn't work. Limiting the ball's velocity to fix the ball disappearing issue also didn't work.</p>
<p>Links on GitHub: <a href="https://github.com/mohanamisra/toss-meh/tree/main/src" rel="nofollow noreferrer">https://github.com/mohanamisra/toss-meh/tree/main/src</a><br />
Demo: <a href="https://toss-game-8d371.web.app/" rel="nofollow noreferrer">https://toss-game-8d371.web.app/</a><br />
Ball disappears permanently on dragging it offscreen and holding for a second or so.</p>
<p><strong>CODE:</strong> The main App</p>
<pre><code>import React, { useRef, useEffect } from 'react';
import p5 from 'p5';
import Matter from 'matter-js'
import './App.css'
import Ball from "./components/Ball.jsx";
import Boundary from "./components/Boundary.jsx";
import Box from "./components/Box.jsx";
import Scoreboard from "./components/Scoreboard.jsx";
import bg from "./assets/images/background.webp";
import candy from "./assets/images/candy.webp";
import skull from "./assets/images/skull.webp";
import ghost from "./assets/images/ghost.webp";
import zombie from "./assets/images/zombie.webp";
import goblin from "./assets/images/goblin.webp";
import vampire from "./assets/images/vampire.webp";
import scoreboard from "./assets/images/scoreboard.webp";
import '@fontsource-variable/pixelify-sans';


let Engine = Matter.Engine;
let World = Matter.Composite;
let Mouse = Matter.Mouse;
let MouseConstraint = Matter.MouseConstraint;
let Collision = Matter.Collision;
let ground, ball, world, engine, mCon, leftWall, rightWall, scoreBoard;
let boxes = [];
let score = 0;
let timer = 42;
let bgImage, ballImage, scoreboardImage;
let monsterImages = [];
let imgIndex = 0;
let myFont;

function sketch(p) {
p.preload = async function() {
bgImage = p.loadImage(bg);
ballImage = p.loadImage(candy);
scoreboardImage = p.loadImage(scoreboard);
monsterImages.push(await p.loadImage(skull));
monsterImages.push(await p.loadImage(ghost));
monsterImages.push(await p.loadImage(zombie));
monsterImages.push(await p.loadImage(goblin));
monsterImages.push(await p.loadImage(vampire));
}

p.setup = function() {
const canvas = p.createCanvas(p.windowWidth, p.windowHeight);
engine = Engine.create();
world = engine.world;
const canvasForMouse = {...canvas};
canvasForMouse.elt = <canvas id="defaultCanvas0" className="p5Canvas" width="1648" height="3660"
style="width: 412px; height: 915px;"></canvas>
console.log(canvas.elt);
console.log(canvasForMouse.elt);
const canvasMouse = Mouse.create(canvasForMouse.elt);
canvasMouse.pixelRatio = p.pixelDensity();
const options = {
mouse: canvasMouse,
}
mCon = MouseConstraint.create(engine, options);

ground = new Boundary(p.width/2, p.height-10, p.width, 20, world);
leftWall = new Boundary(10, p.height/2, 20, p.height, world);
rightWall = new Boundary(p.width - 10, p.height/2, 20, p.height, world);
ball = new Ball(p.width/2, p.height - 100, 40, world);
scoreBoard = new Scoreboard( 10, 10, p.width > 800 ? 400 : p.width - 30, 75, world, scoreboardImage);

World.add(world, [mCon]);
}

setInterval(spawnBoxes, 500);

function spawnBoxes() {
if(boxes.length > 7) {
World.remove(engine.world, boxes[0].body);
boxes.splice(0, 1);
}
let xLoc = p.random(0, p.width);
let yLoc = p.random(100, p.height/2 + 100);
let boxWidth, boxHeight;
let overlapping = false;
if(imgIndex === 3 || imgIndex === 4) {
boxWidth = 105;
boxHeight = 85;
}
else {
boxWidth = 80;
boxHeight = 85;
}
let boxToPush = {
x: xLoc,
y: yLoc,
w: boxWidth,
h: boxHeight,
world: world,
img: monsterImages[imgIndex],
}

for(let j = 0; j < boxes.length; j++) {
let boxToCheckAgainst = boxes[j];
let distance = p.dist(boxToPush.x, boxToPush.y ,boxToCheckAgainst.x, boxToCheckAgainst.y);
if(distance < boxToPush.w + boxToCheckAgainst.w - 80
|| distance < boxToPush.h + boxToCheckAgainst.h - 80) {
overlapping = true;
break;
}
}
if(!overlapping) {
boxes.push(new Box(boxToPush));
imgIndex = (imgIndex + 1) % monsterImages.length;
}

}

p.windowResized = function() {
p.resizeCanvas(p.windowWidth, p.windowHeight);
}

p.draw = function () {
p.background(bgImage);
Engine.update(engine);
rightWall.show(p);
leftWall.show(p);
ground.show(p);
ball.show(p, ballImage);
if(p.frameCount % 60 === 0 && timer > 0) {
timer--;
}
scoreBoard.show(p, score, timer);

for(let i = 0; i < boxes.length; i++) {
boxes.show(p);
}

if(ball.body.position.y < 0 || ball.body.position.y > p.height + 50 || ball.body.position.x < 0 || ball.body.position.x > p.width + 50) {
ball.reset(p);
}

for(let i = 0; i < boxes.length; i++) {
if(Collision.collides(ball.body, boxes.body)) {
console.log("HIT ", i);
World.remove(engine.world, boxes.body);
boxes.splice(i, 1);
ball.reset(p);
score += 1;
}
}
}
}

function App() {
const p5Container = useRef();

useEffect(() => {
const p5Instance = new p5(sketch, p5Container.current);

return() => {
p5Instance.remove();
}
}, []);

return (
<>
<div ref={p5Container} className="container">
</div>
</>
);
}

export default App;
</code></pre>
<p><strong>Ball class:</strong></p>
<pre><code>import Matter from 'matter-js'

export default class Ball {
constructor(x, y, r, world) {
this.body = Matter.Bodies.circle(x, y, r);
Matter.World.add(world, this.body);
this.r = r;
}

show(p, ballImage) {
const pos = this.body.position;
const angle = this.body.angle;
Matter.Body.setSpeed(this.body, Math.min(this.body.speed, 40));
p.push();
p.translate(pos.x, pos.y);
p.rotate(angle);
p.imageMode(p.CENTER);
p.image(ballImage, 0, 0, this.r*2, this.r*2);
p.pop();
}

reset(p) {
Matter.Body.setPosition(this.body, {x: p.width/2, y: p.height - 100});
Matter.Body.setAngle(this.body, 0);
Matter.Body.setSpeed(this.body, 0);
const originalRadius = this.r;
setTimeout(() => {
this.r = 0.00001;
setTimeout(() => {
this.r = originalRadius;
}, 300);
}, 0);
}
}
</code></pre>
<p>How do I resolve this issue?</p>
<p>Thanks in advance!</p>
 

Latest posts

Top