I know of the way in p5.js to create a grid of squares/rect in 2d, however i cannot seem to be able to create this in p5's 3d setting using webGL. I am trying to create a 3d grid "of size 50x50x50 from -400 to 400 in the x-axis and -400 to 400 on the z-axis". However whenever i try to create this, it just seems to stay in a 2d setting.
For example, the 2d method to do this is below, and i want the 3d to be the same consisting of a 16x16 grid
function setup() {
createCanvas(400, 400);
background(220);
for (let x=0;x<width;x+=20){
for (let y=0;y<height;y+=20){
rect(x,y,20,20);
}
}
}
Any help adjusting this code to bring it to 3d would help massively, Ive looked around and cannot find any help on how to do this
Im brand new to 3d primitives and webgl so help would be greatly appreciated.
Below is how I am trying to get it to look
Im aware, camera adjusting would ne needed also, but just wanted to take one step at a time.
Thanks again
You're on the right track, just need 3 ingredients:
use the WEBGL renderer (e.g. canvas(400, 400, WEBGL);)
use box() instead of rect()
use push()/pop() and translate(x, y, z) to draw boxes at the grid coordinates
You can use camera() function to angle the get that perspective.
(You can optionally also use rotateY(), rotateX() calls to rotate the whole coordinate space, but using camera() will preserve the original coordinate system)
function setup(){
createCanvas(400, 400, WEBGL);
background(220);
camera(-200, -200, -200, // camera position (x, y, z)
0 , -100, 0, // camera target (look at position) (x, y, z)
0 , 1, 0); // camera up axis: Y axis here
for (let x=0; x < width; x +=20){
for (let z=0; z < height; z +=20){
push();
// ground plane is XZ, not XY (front plane)
translate(x, 0, z);
box(20);
pop();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"></script>
Optionally you could offset the position of each box based on the centre of the grid so further transformations to the grid will occur from the centre (and not from a corner).
Related
In my Phaser 3 game I have an object which I want to revolve around a point (x,y) in a circle of a specific radius say 5 units. All the tutorials are in Phaser 2, so please help me out.
You can use Phaser.Actions.RotateAroundDistance. There is an example here
In case the link breaks in the future, the parameters are
RotateAroundDistance(point, x, y, angle, distance):
And an exmple use on a group of objects is below:
Phaser.Actions.RotateAroundDistance(group.getChildren(), { x: 400, y: 300 }, 0.02, 200)
My guess would be to set the anchor of the sprite outside the actual sprite. The anchor is the pivot point or "handle" on the sprite, effectively an offset for the x,y position and angle etc. The further away you set the anchor, the bigger the radius will be.
And then add a tween to rotate the angle. So something like:
// setAnchor, x offset = 10 to the right of the sprite, y offset = center of sprite
mysprite.setAnchor(10.0, 0.5);
var tween = game.tweens.add({
targets: mysprite,
angle: 360.0,
duration: 1500,
repeat: 0
});
my question today is about D3 and display text on a globe at a long/lat using Canvas. What I've essentially got is a globe built using mbostock's world tour, which has a list of long/lats and rotates the globe to focus on them. I've already got markers at the points, but what I'd like is to display some text next to all of the points. At this point in time, it's just city names, but the intention is to display more information as the globe is more fully fleshed out. I've created a plunk with the code: https://plnkr.co/edit/CzfoF4bkPv93l3Yxwu3N?p=preview
The relevant code in that plunk is:
return function(t) {
projection.rotate(r(t));
c.clearRect(0, 0, width, height);
c.fillStyle = "#ccc", c.beginPath(), path(land), c.fill();
for (var j = 0; j < points.length; j++) {
c.fillStyle = "#000", c.beginPath(), path(points[j]), c.fill();
}
c.strokeStyle = "#000", c.lineWidth = 2, c.beginPath(), path(globe), c.stroke();
};
Basically what I'm asking for is a way to display text slightly to the right of specific long/lat coordinates on a globe using canvas. Any and all help is greatly appreciated. Thank you in advance!
The easiest way would be to add a the text in your tween function after you draw the features:
c.fillText(points[i].location,projection(points[i].coordinates)[0]+10,projection(points[i].coordinates)[1]+2);
With the variable projection, projection([long,lat]) returns [x,y], in other words it gives the forward projection of a given point represented by latitude and longitude, returning a coordinate in map coordinate space (your svg or canvas).
Once you have the projected coordinates, you can manipulate those as needed. In this case I added ten to the x value to push the place name further to the right. See this plunker.
I'm working on a game for a university assignment. The idea is that you defend the centre circle from the incoming asteroids (lines) by drawing a line (click, drag & release to draw a line) which blocks them. An asteroid hitting a line should destroy both the asteroid the line.
The problem I'm currently having is that the collision isn't being detected.
I have arrays of objects of both lines & asteroids. The lines consist of simply start & end x & y, the asteroids consist of a random speed & a random angle (their incoming angle) - the context is rotated, the asteroid drawn, & then it reset for the next line.
To detect collision, I use getImageData & check in front of the asteroids however many pixels the line will progress in that iteration (basically, their speed) & if the colour is red, it will destroy the asteroid - I haven't got round to destroying the line yet, will tackle that hurdle when I come to it (suggestions are welcome though).
function asteroids_draw() {
for (var i = 0; i < asteroids.length; i++) {
// Drawing setup
context.save();
context.translate(width / 2, height / 2);
context.rotate(asteroids[i].angle);
// Detecting close asteroids
if ((asteroids[i].distance - asteroids[i].speed) < planet.size) {
asteroids.splice(i, 1);
game_life_lost();
context.restore();
return;
} else if ((asteroids[i].distance - asteroids[i].speed) < 150){
asteroids[i].colour = '#FF0000';
}
// Scanning ahead for lines
for (var j = 0; j < asteroids[i].speed; j++) {
if (context.getImageData(asteroids[i].distance - j, 0, 1, 1).data[0] == 255) {
asteroids.splice(i, 1);
context.restore();
return;
}
}
// Drawing asteroid
context.beginPath();
context.moveTo(asteroids[i].distance -= asteroids[i].speed, 0);
context.lineTo(trig, 0);
context.strokeStyle = asteroids[i].colour;
context.stroke();
context.closePath();
context.restore();
}
}
The problem is, the asteroids never collide with the lines & I can't for the life of me see why, or see another simple way of doing it. Any advice would be much appreciated, thanks in advance.
I think your problem is that when you rotate the context, previously drawn items(lines) don't get rotated, only objects drawn after the rotation are rotated. See this page for more info.
You could try performing your asteroid/line intersection test before you translate and rotate the canvas, and use cosine and sine to find the x and y coordinates of the pixels you want to get image data from.
var pixelLocation =
[Math.cos(asteroids[i].angle) * j, Math.sin(asteroids[i].angle) * j];
if (context.getImageData(pixelLocation[0], pixelLocation[1], 1, 1).data[0] == 255) {
Just make sure your angle is in radians before passing to cos and sin.
I thought about the problem some more, & realised this method of doing things definitely isn't the best way. It should be doable without a view - a la Model View Controller design pattern. The best way to solve it would be to use maths!
There's simple maths for the intersection of two lines, but this needs intersection of two lines in a range. I found an algorithm which simplifies this further, using eight coordinates - the start x & y & the end x & y of the two lines.
I've posted the results. Thanks for the help.
OK, I admit I tried to be clever: I thought if I overrode Shape's drawFunc property I could simply draw whatever inside a rectangle and still use KineticJS's click detection. Here's my attempt:
var shape = new Kinetic.Shape({
drawFunc: function(context) {
var id = 26; // Id of a region inside composite image.
context.beginPath();
context.rect(0, 0, w, h);
context.closePath();
this.fill(context);
this.stroke(context);
context.drawImage(copyCanvas, (id % 8) * w, flr(id / 8) * h,
w, h, 0, 0, w / 2, h / 2);
},
draggable: true
});
So, the idea was to draw a rectangle, and use drawImage() to draw something on top of the rectangle (like a texture, except it changes from time to time because copyCanvas itself changes). All the meanwhile, I expected event handling (drag-n-drop, in particular) to still 'just work'. Well, here's what happens: the part of the rectangle not covered by my drawImage() correctly detects clicks. However, the one fourth of the rectangle that is covered by the image refuses to respond to clicks! Now, my question is why? I dug into the KineticJS code, and looked to me that click detection simply means drawing to a buffer and seeing if a given x, y point has non-zero alpha. I can't see how this could be affected by my drawing an image on top of my rectangle.
Any ideas what's going on?
OK, so I went ahead and looked at the source code. Here's the definitive answer:
KineticJS assigns a random and unique RGB color to each shape that's created using a global map from RGB colors to shape objects. The draw() function of the shape is called twice: once with the 'real' canvas, and once with a 'buffer' canvas used for hit detection. When using the 'buffer' canvas, KineticJS switches the stroke and fill colors to the unique RGB color of the given shape. The same 'buffer' canvas is used for all shapes on a layer. Thus hit detection simply becomes reading the RGB value of a given point and looking up the corresponding shape in the global map. Now, in my example I drew an image in a way that circumvented KineticJS's juggling of colors used for hit detection. Thus, when I clicked on the image area, KineticJS saw some unknown RGB color on the buffer canvas with no known shape assigned to it.
The solution is not to draw the image for the 'buffer' (or 'hit detection') phase: a simple rectangle will do. In case you're wondering, here's the correct code for the drawFunc:
var width = 200;
var height = 100;
var myShape = new Kinetic.Shape({
drawFunc: function(context) {
if (layer.bufferCanvas.context == context) {
context.beginPath();
context.rect(0, 0, width, height);
context.closePath();
this.fill(context);
this.stroke(context);
} else {
context.drawImage(someCanvasWithAnythingOnIt, 0, 0, width, height,
0, 0, width, height);
}
}});
Can I collect my own reward?
I think your problem lies in the order. There is a depth associated with each object that you draw and the default ordering is like a stack, last drawn is on top.
Now that you have modified the code, making 2 draws inside the shape draw function, I still think the ordering is preserved and hence, the object is not able to detect the input. Try changing the order, i.e. draw image first and then the rectangle and see if the problem is solved.
Else, share a jsFiddle for an example.
The question title may be vague. Basically, imagine a racing game built in canvas. The track takes up 10,000 x 10,000 pixels of screen space. However the browser window is 500 x 500 pixels. The car should stay centered in the browser and the 'viewable' area of the 10,000 x 10,000 canvas will change. Otherwise the car would just drive off the edge at disappear.
Does this technique have a name?
What are the basic principles to make this happen?
If the car should stay at the same position (relative to the canvas' position), then you should not move the car. Instead, move the background picture/track/map to the other side.
Causing your eyes to think the car moves right can be done by either moving the car to the right, or by moving the map to the left. The second option seems to be what you want, since the car won't move whereas the viewable area (i.e. the map) will.
This is a quick demo from scratch: http://jsfiddle.net/vXsqM/.
It comes down to altering the map's position the other way round:
$("body").on("keydown", function(e) {
if(e.which === 37) pos.x += speed; // left key, so move map to the right
if(e.which === 38) pos.y += speed;
if(e.which === 39) pos.x -= speed;
if(e.which === 40) pos.y -= speed;
// make sure you can't move the map too far.
// clamp does: if x < -250 return -250
// if x > 0 return 0
// else it's allowed, so just return x
pos.x = clamp(pos.x, -250, 0);
pos.y = clamp(pos.y, -250, 0);
draw();
});
You can then draw the map with the position saved:
ctx.drawImage(img, pos.x, pos.y);
If you're looking for a way to actually move the car when the map cannot be moved any further (because you're driving the car close to a side of the map), then you'd have to extend the clamping and also keep track of when the car should be moved and how far: http://jsfiddle.net/vXsqM/1/.
// for x coordinate:
function clamp2(x, y, a, b) { // x = car x, y = map x, a = min map x, b = max map x
return y > b ? -y : y < a ? a - y : x;
}
The position clamping then becomes a little more complex:
// calculate how much car should be moved
posCar.x = clamp2(posCar.x, posMap.x, -250, 0);
posCar.y = clamp2(posCar.y, posMap.y, -250, 0);
// also don't allow the car to be moved off the map
posCar.x = clamp(posCar.x, -100, 100);
posCar.y = clamp(posCar.y, -100, 100);
// calculate where the map should be drawn
posMapReal.x = clamp(posMap.x, -250, 0);
posMapReal.y = clamp(posMap.y, -250, 0);
// keep track of where the map virtually is, to calculate car position
posMap.x = clamp(posMap.x, -250 - 100, 0 + 100);
posMap.y = clamp(posMap.y, -250 - 100, 0 + 100);
// the 100 is because the car (circle in demo) has a radius of 25 and can
// be moved max 100 pixels to the left and right (it then hits the side)
Two things:
Canvas transformation methods
First, the canvas transformation methods (along with context.save() and context.restore() are your friends and will greatly simplify the math needed to view a portion of a large 'world`. If you use this approach, you can get the desired behavior just by specifying the portion of the world that is visible and the world-coordinates of everything you want to draw.
This is not the only way of doing things* but the transformation methods are meant for exactly this kind of problem. You can use them or you can reinvent them by manually keeping track of where your background should be drawn, etc., etc.
Here's an example of how to use them, adapted from a project of mine:
function(outer, inner, ctx, drawFunction) {
//Save state so we can return to a clean transform matrix.
ctx.save();
//Clip so that we cannot draw outside of rectangle defined by `outer`
ctx.beginPath();
ctx.moveTo(outer.left, outer.top);
ctx.lineTo(outer.right, outer.top);
ctx.lineTo(outer.right, outer.bottom);
ctx.lineTo(outer.left, outer.bottom);
ctx.closePath();
//draw a border before clipping so we can see our viewport
ctx.stroke();
ctx.clip();
//transform the canvas so that the rectangle defined by `inner` fills the
//rectangle defined by `outer`.
var ratioWidth = (outer.right - outer.left) / (inner.right - inner.left);
var ratioHeight = (outer.bottom - outer.top) / (inner.bottom - inner.top);
ctx.translate(outer.left, outer.top);
ctx.scale(ratioWidth, ratioHeight);
ctx.translate(-inner.left, -inner.top);
//here I assume that your drawing code is a function that takes the context
//and draws your world into it. For performance reasons, you should
//probably pass `inner` as an argument too; if your draw function knows what
//portion of the world it is drawing, it can ignore things outside of that
//region.
drawFunction(ctx);
//go back to the previous canvas state.
ctx.restore();
};
If you are clever, you can use this to create multiple viewports, picture-in-pictures, etc. of different sizes and zoom in and out on stuff.
Performance
Second, as I commented in the code, you should make sure your drawing code knows what portion of your larger 'world' will be visible so that you don't do a lot of work trying to draw things that will not be visible.
The canvas transformation methods are meant for solving exactly this kind of problem. Use 'em!
*You will likely have problems if your world is so large that its coordinates cannot fit in an appropriate integer. You'll hit that problem roughly when your world exceeds billion (10^9) or a long trillion (10^18) pixels in any dimension, depending on whether the integers are 32- or 64-bit. If your world isn't measured in pixels but in 'world units', you'll run into problems when your world's total size and smallest feature scale lead to floating point inaccuracies. In that case, you will need to do extra work to keep track of things... but you'll probably still want to use the canvas transformation methods!
My very first game was a racing game where I moved the background instead of the car and although I want to think now that I had my reasons to make it so... I just didn't know better.
There are a few techniques that you need to know to achieve this well.
Tiled background. You need to make your track out of smaller pieces that tiled. To To draw 10,000 x 10,000 pixels is 100MPix image usually such image will have 32bit depth (4 bytes) this will end up being 400MB in memory. Compressions like PNG, JPEG won't help you since these are made to store and transfer images. They cant be rendered to a canvas without decompressing.
Move the car along your track. There is nothing worst then moving the BG under the car. If you need to add more features to your game like AI cars... now they will have to move along the map and to implement car collisions you need to make some not hard but strange spacial transformations.
Add camera entity. The camera needs to have position and viewport size (this is the size of your canvas). The camera will make or break your game. This is the entity that will give you the sense of speed in the game... You can have a camera shake for collisions, if you have drifts if your game the camera can slide pass the desired position and center back to the car, etc. Of course the most important thing will be tracking the car. Some simple suggestions I can give you are to not put the car in dead center of the camera. put the car a little behind so you can see a bit more what's in front of your. The faster the car moves the more you should offset the camera. Also you can't just compute the position of the camera instead compute desired position and slowly per frame move the current camera position to the desired position.
Now when you have camera and a large tiled map, when you draw the tiles you have to subtrack the camera position. You can also compute which tiles are not visible and skip them. This technique will allow you do extend your game with even larger maps or you can stream your map where you don't have all the tiles loaded and load in advance on background (AJAX) what will be visible soon.