Hello.
are there any codes for the dimensional version of this game? For example, I want to look at the codes and make an example myself.
Hello.
are there any codes for the dimensional version of this game? For example, I want to look at the codes and make an example myself.
You can find it here https://codepen.io/zimjs/pen/dyBYbMq - just add it to a ZIM template from here https://zimjs.com/code
thanks for the answer. do you have an example without threejs? also how did you create the maze image?
Can you make a very short video for this?
It doesn't have to be a very complicated maze. A very small maze is enough. How is the start set? How is the end set?
How is the ball's contact with the walls detected? Is the black line collision detected? I don't understand much because my English is not good.
But I can think of many games with this maze.
Oh... I see - just a flat maze. Yes, we made one like that a long time ago... but this version has updated code. We did do an Explore video about the code.
See if that has enough information for you. It talks about how we made the current maze as well.
Oh... I just found a later remake of the old maze at https://codepen.io/danzen/pen/ROPxNQ
Console gives an error. The ball moves to the left.
When I click on the warning in the console,
"const data = ctx.getImageData(x, y, 1, 1).data;"
it gives this error.
That is just a warning... I think you can ignore it. Stupid warnings.
We already dealt with the willReadFrequently issue in CreateJS and ZIM - maybe you just need to add another parameter to the getImageData - can't remember if there is one for that - but have never noticed the difference so it is just google being the google police.
Ok thank you. This is very nice. I hadn't noticed it before.
Yes! I have always loved mazes and so it was a dream to be able to turn a hand-drawn maze into a working interactive maze. And I worked out how to do it, all by myself ;-).
I can't thank you enough. Thanks to you, I have made many interactive activities and games. Thank you very much.
// given F (Frame), S (Stage), W (width), H (height)
// put code here
new Label("RING MAZE", 40, "Fascinate Inline", pink)
.pos(0,0,CENTER,TOP)
.noMouse();
const button = new Button({label:"PLAY", corner:6}).sca(.5).pos(0,0,CENTER,BOTTOM).tap(()=>{
button.removeFrom();
timeout(1, ()=>{
ball.dynamic = true
})
});
STYLE = {once:true, italic:true, bold:true}
const win = new Label("CONGRATULATIONS",20,null,yellow)
.reg(CENTER)
.pos(0,0,CENTER,BOTTOM)
.cache()
.sca(0);
// we do not want the content to get in the way of the OrbitControls
// so easiest to add to container and set noMouse() - we can still track mouse position in Ticker
const wall = new Container().addTo(stage)
wall.x=150
wall.y=150
const holder = new Container(W,H).addTo(wall).noMouse();
// MAZE
// we can load in ANY picture of a maze as long as the walls are different than the backing
// we could even load two pictures... a hidden one to represent the walls and a visual more complex one
// we then use physics to apply a force to the ball to follow the mouse
// and we make physic walls dynamically around the ball's position
// the walls are placed only on the non-background color
// the walls are removed as the ball leaves the area and new ones are made
// thanks https://www.mazegenerator.net/
// note: we made a vertical maze to start at the top and bottom
// then rotated the image to start at the sides
var maze = new Pic("maze.png").addTo(holder).cache();
// cache the image so we have a second canvas to use later
// this allows us to get the color of the pixel under the ball
// without getting the color of the ball ;-)
// new Label("START",25,null,white).pos(90,-20,LEFT,CENTER,holder);
// new Label("END",25,null,white).pos(60,-20,RIGHT,CENTER,holder);
//new Rectangle(W,H,new GradientColor([blue,green,orange,yellow])).addTo(holder).ble("darken");
// create a Physics instance to handle making the ball bounce off walls
// we will make walls dynamically only in the area of the ball
// that way we don't make thousands of walls that we don't need
// use the default outer walls and set gravity to 0
var physics = new Physics(0);
var ball = new Circle(8, purple).loc(200,20,holder).addPhysics(false, 2);
// add an optional little finder
new Circle(30,white.toAlpha(.3)).center(ball,0).wiggle("scale",1,.1,.2,.7,1.5);
const end = new Rectangle(50,22,red)
end.x=maze.width/2-end.width
end.y=maze.height
holder.addChild(end)
// create a Ticker to constantly apply a force to the ball
// and make the walls near the ball
// the factor is for the force
// balance the speed with a tendency to go through walls if too fast
const emitter = new Emitter({
startPaused:true,
obj:new Circle(ball.radius*1.3,clear,purple,3),
animation:{scale:{min:.5, max:1.5}},
force:{min:.5, max:1.5},
gravity:0
}).addTo(holder);
const factor = .005; // force is incremental in time (make small)
const max = .5; // limit the mouse distance (which limits force)
Ticker.add(()=>{
// make the walls
makeWalls();
// apply a force towards the mouse
// do not use stage.mouseX and stage.mouseY
// as they do not catch touch location
// use any mouse event's mouseX and mouseY instead
// we did that and stored the values in mouseX and mouseY
let dX = constrain((F.mouseX-ball.x)*factor, -max, max);
let dY = constrain((F.mouseY-ball.y)*factor, -max, max);
ball.force(dX, dY);
if (ball.hitTestBounds(end)) {
emitter.loc(ball).spurt(20);
ball.dynamic = false;
ball.vis(false);
ball.body.loc(maze.width/2-ball.width,maze.y);
win.animate({
wait:.8,
time:1.2,
props:{scale:1},
rewind:true,
ease:"elasticOut"
});
timeout(3.5, ()=>{
ball.dynamic = true;
ball.vis(true);
emitter.loc(ball).spurt(20);
});
}
});
// we want to find the color of the maze picture around where the ball is
// we will put a wall at anywhere that is not the background color
// so we access the context 2D of the cached picture
var ctx = maze.cacheCanvas.getContext('2d');
var num = 20; // test a 10x10 grid around the ball
var space = 1; // the spacing of the points on the grid
var radius = 1; // the radius of a wall placed at a point
var walls = []; // an array to keep track of the active walls
function makeWalls() {
// remove any walls from the last time
loop(walls, wall=>{
physics.remove(wall);
});
walls = [];
// loop through our grid
loop(num, i=> {
loop(num, j=> {
// locate the x and y point on the grid for this i,j index
const x = ball.x - num / 2 * space + i*space;
const y = ball.y - num / 2 * space + j*space;
// get the color data of the pixel at this grid location
const data = ctx.getImageData(x, y, 1, 1).data;
// Physics lets you automatically map physics bodies to ZIM objects
// but in this case, we do not need visual objects
// and we are creating many objects - so do not make the ZIM objects
// Physics has methods to add only physics objects
// so this is what we do in this case
// make the wall if the color is darker than the background color
if (data[0] < 150) {
let wall = physics.makeCircle(radius, false);
//wall.loc(x,y);
// add the wall to our array of walls
walls.push(wall);
}
});
});
}
Hello. When I try to bring the maze to the middle, I can't get it. Actually, I get it but it doesn't work. Can you check my code?
Also, can I create a new maze once one is solved?
Physics objects must be on the stage or in an unscaled Container that is at 0,0 on the stage. So you can't shift your holder. Just shift the object inside and put holder at 0,0.
You should be able to change the maze once solved.
I couldn't do it. If you have a sample code I would be grateful.
Okay - a few things.
We do not need the containers - those were there for other reasons. So, I have just centered the maze Pic on the stage, and the ball is on the stage too.
The test of the wall color happens relative to the maze drawing. But because you have moved the maze to the center, we have to use where the ball is relative to the maze coordinates. So we have used maze.globalToLocal(ball.x, ball.y) to get us the proper point relative to the maze. And then when we find a wall, we have to convert that position back to the global position to locate the wall globally for the physics engine (which only works on global coordinates) so we use maze.localToGlobal(x,y).
All our mazes have the ball on the maze drawing. The background of the maze drawing is white (or grey, or some color not near black) so that a wall will not be made when the ball is starting. On your example, the ball is off the maze so it does not find a light color but rather no color - which is black and so walls are made.
To solve this, I have put in a test to see if the ball is within the bounds of the maze - but even so, there is a gap in the white at the entrance and the exit. I would color that gap in white in your image editor - otherwise, the fix of adjusting the top and bottom tests by 2 pixels seems to work, but may not always.
Here is the working code - adjusted in a half dozen places.
You also have a cheating issue where they can go around the maze... To solve this you can add rectangles at the sides that go right across the height of the page and addPhysics() to them. Make them the same as the background color or .vis(false) them. I have not done this in the code below.
new Label("RING MAZE", 40, "Fascinate Inline", pink)
.pos(0, 0, CENTER, TOP)
.noMouse();
const button = new Button({label: "PLAY", corner: 6}).sca(.5).pos(0, 0, CENTER, BOTTOM).tap(() => {
button.removeFrom();
timeout(1, () => {
ball.dynamic = true;
});
});
STYLE = {once: true, italic: true, bold: true};
const win = new Label("CONGRATULATIONS", 20, null, yellow)
.reg(CENTER)
.pos(0, 0, CENTER, BOTTOM)
.cache()
.sca(0);
// MAZE
// we can load in ANY picture of a maze as long as the walls are different than the backing
// we could even load two pictures... a hidden one to represent the walls and a visual more complex one
// we then use physics to apply a force to the ball to follow the mouse
// and we make physic walls dynamically around the ball's position
// the walls are placed only on the non-background color
// the walls are removed as the ball leaves the area and new ones are made
// thanks https://www.mazegenerator.net/
// note: we made a vertical maze to start at the top and bottom
// then rotated the image to start at the sides
const maze = new Pic("maze.png").center().cache();
// cache the image so we have a second canvas to use later
// this allows us to get the color of the pixel under the ball
// without getting the color of the ball ;-)
// new Label("START",25,null,white).pos(90,-20,LEFT,CENTER,holder);
// new Label("END",25,null,white).pos(60,-20,RIGHT,CENTER,holder);
//new Rectangle(W,H,new GradientColor([blue,green,orange,yellow])).addTo(holder).ble("darken");
// create a Physics instance to handle making the ball bounce off walls
// we will make walls dynamically only in the area of the ball
// that way we don't make thousands of walls that we don't need
// use the default outer walls and set gravity to 0
const physics = new Physics(0);
// physics.debug()
const ball = new Circle(8, purple).pos(-20,80,CENTER).addPhysics(false, 2);
ball.startX = ball.x;
ball.startY = ball.y;
// add an optional little finder
new Circle(30, black.toAlpha(.3)).center(ball, 0).wiggle("scale", 1, .1, .2, .7, 1.5);
const end = new Rectangle(50, 22, red).pos(-20,-25,CENTER,BOTTOM,maze).addTo(S)
// create a Ticker to constantly apply a force to the ball
// and make the walls near the ball
// the factor is for the force
// balance the speed with a tendency to go through walls if too fast
const emitter = new Emitter({
startPaused: true,
obj: new Circle(ball.radius * 1.3, clear, purple, 3),
animation: {scale: {min: .5, max: 1.5}},
force: {min: .5, max: 1.5},
gravity: 0
})
const factor = .005; // force is incremental in time (make small)
const max = .5; // limit the mouse distance (which limits force)
Ticker.add(() => {
// make the walls
makeWalls();
// apply a force towards the mouse
// do not use stage.mouseX and stage.mouseY
// as they do not catch touch location
// use any mouse event's mouseX and mouseY instead
// we did that and stored the values in mouseX and mouseY
let dX = constrain((F.mouseX - ball.x) * factor, -max, max);
let dY = constrain((F.mouseY - ball.y) * factor, -max, max);
ball.force(dX, dY);
if (ball.hitTestBounds(end)) {
emitter.loc(ball).spurt(20);
ball.dynamic = false;
ball.vis(false);
ball.body.loc(ball.startX, ball.startY);
win.animate({
wait: .8,
time: 1.2,
props: {scale: 1},
rewind: true,
ease: "elasticOut"
});
timeout(3.5, () => {
ball.dynamic = true;
ball.vis(true);
emitter.loc(ball).spurt(20);
});
}
});
// we want to find the color of the maze picture around where the ball is
// we will put a wall at anywhere that is not the background color
// so we access the context 2D of the cached picture
var ctx = maze.cacheCanvas.getContext('2d');
var num = 20; // test a 10x10 grid around the ball
var space = 1; // the spacing of the points on the grid
var radius = 1; // the radius of a wall placed at a point
var walls = []; // an array to keep track of the active walls
function makeWalls() {
// remove any walls from the last time
loop(walls, wall => {
physics.remove(wall);
});
walls = [];
// loop through our grid
loop(num, i => {
loop(num, j => {
const pointIn = maze.globalToLocal(ball.x, ball.y);
// locate the x and y point on the grid for this i,j index
const x = pointIn.x - num / 2 * space + i * space;
const y = pointIn.y - num / 2 * space + j * space;
if (x < 0 || y < 2 || x > maze.width || y > maze.height-2) return;
// get the color data of the pixel at this grid location
const data = ctx.getImageData(x, y, 1, 1).data;
// Physics lets you automatically map physics bodies to ZIM objects
// but in this case, we do not need visual objects
// and we are creating many objects - so do not make the ZIM objects
// Physics has methods to add only physics objects
// so this is what we do in this case
// make the wall if the color is darker than the background color
if (data[0] < 150) {
let wall = physics.makeCircle(radius, false);
const pointOut = maze.localToGlobal(x,y);
wall.x = pointOut.x,
wall.y = pointOut.y;
// add the wall to our array of walls
walls.push(wall);
}
});
});
}
Another issue, for example ["maze.png","maze2.png","maze3.png"] I want to add these to the maze in order. When the goal is reached in the maze in the first image, when a button is clicked, the other image maze should be created. I know I have troubled you a lot. It would be great if there is a sample code.