Im trying to make a side-scroller game and I got stuck on the running background part. I've looked for solutions and I've discovered some , but they were using javascript not the p5 library.
I started from the tutorials found on The Coding Train , and looked over all the examples and references on their site.
Although I could avoid this by using something else, just for the sake of it being here in case someone gets stuck on the same issue, could anyone offer a solution to this in p5? Disclaimer: Im a total noob p5.js.
later edit : By running background i mean moving Background image in a loop from left to right
Honestly, from the discussion we had in the comments, it sounds like you're overthinking it.
The general approach to animation (that tutorial is for Processing, but the principles apply to P5.js as well) is as follows:
Step 1: Create a set of variables that represent the state of your scene.
Step 2: Use those variables to draw your scene every frame.
Step 3: Change those variables over time to make your scene move.
You already know what to do: load an image that contains your background, then draw that image, and move it a little bit each frame.
You've said you want to call the background() function instead of the image() function, which doesn't make a ton of sense. The background() function is not any more efficient than the image() function. In fact, the background() function just calls the image() function for you!
From the P5.js source:
p5.prototype.background = function() {
if (arguments[0] instanceof p5.Image) {
this.image(arguments[0], 0, 0, this.width, this.height);
} else {
this._renderer.background.apply(this._renderer, arguments);
}
return this;
};
P5.js simply checks whether the argument is an image, and if so, calls the image() function for you. So it doesn't really make sense to say that using the image() function is "less efficient" than using the background() function.
Taking a step back, you should really avoid thinking about these kinds of micro-optimizations until you A: understand the problem and B: actually have a problem. Don't make assumptions about "efficiency" until you've actually measured your code for performance.
Anyway, back to your question. You also said that you're loading the image twice, which you shouldn't have to do. You can just load the image once (make sure you do that in the setup() function and not the draw() function, and then draw that image twice:
var img;
function preload() {
img = loadImage("image.jpg");
}
function setup() {
image(img, 0, 0);
image(img, 100, 100);
}
And since you can draw two images, you'd then just draw them next to each other. Here's an example using colored rectangles to show the approach more clearly:
var offsetX = 0;
function setup() {
createCanvas(200, 200);
}
function draw() {
background(0);
fill(0, 255, 0);
rect(offsetX, 0, width, height);
fill(0, 0, 255);
rect(offsetX + width, 0, width, height);
offsetX--;
if(offsetX <= -width){
offsetX = 0;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>
There are other ways to do it, like creating an image that contains the wrapping itself. But the general approach is pretty much the same.
If you're still stuck, please try to break your problem down into smaller pieces like I've done here. For example, notice that I created a simple sketch that deals with images, and another simple sketch that deals with moving rectangles. Then if you get stuck, please post a MCVE in a new question post and we'll go from there. Good luck.
Maybe it is a late answer.. but you can make the environment 3D and then move the camera.
Docs: https://p5js.org/reference/#/p5/camera
Example:
function setup() {
createCanvas(windowWidth - 200, windowHeight - 200, WEBGL);
background(175);
frameRate(30);
}
function draw() {
background(175);
//move the camera Xaxis when mouse is moved
let camX = map(mouseX, 0, width, 0,width);
camera(camX, 0, (height/2.0) / tan(PI*30.0 / 180.0), camX, 0, 0, 0, 1, 0);
normalMaterial();
noStroke();
ambientLight(251,45,43);
box(100, 100, 50);
ang += 0.3;
rotateY(ang * 0.03);
}
Keep calm and Happy Coding!
Related
I'm looking for a way to limit what gets done in the draw loop.
I have a system where when I click, it add's a rect.
This rect then starts spawning circles that move.
since the rect does not change location, it isn't ideal to redraw it in every frame.
Is there a way to put the rects on a different layer of sorts, or is there another mechanism that I can use to limit the rect-drawing without impeding the circle-drawing?
I've tried with createGraphic to make a background with the rects, but I can't make the 'foreground' where the circles reside to be transparant.
Curious about this I tried myself. My idea was simply grabbing the canvas and interacting with it regardless of p5.js.
My result was that the draw... in this case ctx.fillRect did not render on screen.
However the fillStyle was changed.
Canvas is surprisingly efficient as well as WebGL and can handle the performance usually... unless you are rendering hundreds(mobile) to thousands(laptop/desktop) of objects.
I would have liked to have a better outcome but I think it was worthwhile posting what I had tried and my outcome nonetheless.
//P5 Setup
function setup(){
createCanvas(1500, 750);
background('rgba(0, 0, 0, 0.3)');
stroke(255);
fill(255)
doNonP5Drawing();
}
//Render
function draw(){
background(0);
frame();
}
function doNonP5Drawing(){
let canvas = document.querySelector('canvas'),
ctx = canvas.getContext('2d');
ctx.fillStyle="red";
ctx.fillRect(canvas.innerWidth/2 - 100,canvas.innerHeight/2 - 100,200,200);
}
I'm working on a Quad-copter and for testing purposes I have decided to use Processing to give me a visual example of what the micro-controller is processing and calculating (and possibly some control algorithm simulation later on). So I have made a simple model of a Quad-copter and was displaying it in the upper right of my screen. In the "rest position," I want a perfect side view of the Quad-copter, like this:
Instead, I get an image like this:
The second image was when I rendered the Quad in the upper right, and the first is when I rendered it dead center in the window.
I understand what is happening here but I don't know how to fix it. The rendering system assumes my point of view is dead center in the screen, so anything up and to the right of my point of view is seen from underneath and in the front a little. I poked around on the Reference tab on their website and nothing seems to do exactly what I want. I would think that there would be a solution to this, but I currently can't find one.Does anyone know how to fix this? Thanks.
It sounds like you might be looking for the ortho() function. You can read about it in the reference here.
Sets an orthographic projection and defines a parallel clipping volume. All objects with the same dimension appear the same size, regardless of whether they are near or far from the camera.
Consider this little example program without calling the ortho() function:
void setup(){
size(500, 500, P3D);
}
void draw(){
background(255);
translate(300, 100);
noFill();
stroke(0);
box(100, 100, 100);
}
Now let's add the call to the ortho() function:
void setup(){
size(500, 500, P3D);
}
void draw(){
background(255);
translate(300, 100);
ortho();
noFill();
stroke(0);
box(100, 100, 100);
}
You now no longer see the "depth" of the square. You can add parameters to the ortho() function to make it do exactly what you want, but those are the basics.
Alternatively, you could do something like setup a view that you draw to the middle of, and then draw that view in the upper-right corner of your main view.
I'm working on a game which involves spaceships. We're using OGRE and Bullet.
The player's ship is supposed to pitch and yaw with the mouse -- moving the mouse left will yaw the ship left, for example. The problem is, I can't figure out how to apply torque in Bullet.
Before we started integrating Bullet, our solution was to directly modify the OGRE scene node like so (using OIS for input):
bool Game::mouseMoved(const OIS::MouseEvent &event) {
shipNode->yaw(Ogre::Degree(-event.state.X.rel));
shipNode->pitch(Ogre::Degree(event.state.Y.rel));
return true;
}
And that worked fine.
Now that we're using Bullet, my plan was to apply torque to the rigid body, like so:
bool Game::mouseMoved(const OIS::MouseEvent &event) {
//shipNode->yaw(Ogre::Degree(-event.state.X.rel));
//shipNode->pitch(Ogre::Degree(event.state.Y.rel));
shipRigidBody->applyTorqueImpulse(btVector3(-event.state.X.rel, event.state.Y.rel, 0));
return true;
}
Then, after stepping the simulation, update the scene node's orientation, like so:
void Game::update(const Ogre::FrameEvent &event) {
// ... snip ...
physicsWorld->stepSimulation(event.timeSinceLastFrame, 10);
btTransform shipTransform;
shipMotionState->getWorldTransform(shipTransform);
shipNode->setPosition(shipTransform.getOrigin().x(), /* ... snip ... */);
shipNode->setOrientation(shipTransform.getRotation().getAngle(), shipTransform.getRotation().getAxis().x(), /* ... snip ... */);
}
But with this setup, I can't orient the ship at all.
Next, I tried just applying a torque impulse of (100, 10, 200) every frame no matter what, then printing out shipTransform.getRotation().getAngle()). Always 0. At this point, I became extremely confused, since you'd think that always applying torque would make the body's orientation change.
So, my question is: Am I missing something stupid about btRigidBody::applyTorqueImpulse()? Or am I barking up the wrong tree entirely?
PS: shipRigidBody is constructed like this:
shipMotionState = new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 100, 0)));
shipShape = new btBoxShape(btVector3(50, 20, 75));
shipShape->calculateLocalInertia(btScalar(1), btVector3(0, 0, 0));
shipRigidBody = new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(1, shipMotionState, shipShape));
I also have code which accelerates the ship forwards or backwards based on keyboard input, and that code (which uses btRigidBody::applyCentralImpulse()) is working fine.
I am using the HTML5 canvas to draw an organisational structure. Each node includes an image, the URL of which is obtained form a database. I have a little function that handles the loading of the image, and that adds it to the canvas once loaded. It looks like this (note that the important bit is the onload listener added to the image):
(function (i, ctx, DrawId) {
var img = new Image();
img.src = REPORT.Structures[i].Managers[j].Employee.PhotoUrl; //get URL from object constructed elsewhere
img.onload = function () { //attach onload event
ctx.drawImage(img, x, y, width, height);//draw image on the canvas. x, y width and height are calculated inside the onload, but I have removed the calculations to improve readability
}
}
})(i, ctx, REPORT.DrawId);
This works great, except that the images all pop into existence at random as they finish loading. It looks very untidy.
I am looking for a way to make this look smoother. My first instinct is to try and create a fade in effect, but I can't think of how to do this without redrawing the canvas over and over.
I have thought of trying something like this:
img.onload = function () {
var counter = 1;
var width = 30 * REPORT.ZoomLevel;
var height = 30 * REPORT.ZoomLevel;
function fadeInImage(ctx, img, x, y, width, height) {
ctx.globalAlpha = 0.1 * counter;
ctx.drawImage(img, x, y, width, height);
counter++;
if (counter == 10)
clearTimeout(fadeInImageTimeout);
console.log(counter);
}
var fadeInImageTimeout = setInterval(function () { fadeInImage(ctx, img, x, y, width, height); }, 100);
}
This works, but I am also worried that if it does, it will be too processing heavy.
Has anyone tried doing something like this? Any suggestions would be much appreciated.
PS:
I am currently considering only having one interval that draws all images. Each image will be pushed into an array, and all elements in the array will be drawn once all images are loaded. Will keep you updated.
PPS:
I have managed to get it working as I mentioned in the PS above. It does not seem to affect the performance though. I guess the lag I am still experiencing is related to the image loading time. I'll try and strip out the code into a bare bones version and post it here a bit later, but if anyone can see a flaw in my approach, please do not hesitate to point it out. Again, any suggestions would be greatly appreciated.
Am trying to set a "dirty zone" on my canvas to prevent the repainting of unmoved items (background image, static items, etc.)
i.e. only the background painted behind a moving player needs to be redrawn
EDIT: As suggested, here's the jsfiddle of it
http://jsfiddle.net/7kbzj/3/
The "update" method doesn't work out there, so it's moveSprite() you can get run by clicking the "move sprite" link... Basically, the clipping zone shouldmove by 10px to the right each time you click. Clipping mask stays at initial position, only the re-paint occurs. Weird o_O
So as I init my canvas, once the background is painted, set I use the ctx.save() method:
function init() {
canvas = document.getElementById('kCanvas');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(0,128,0)";
ctx.fillRect (0,0,320,240);
ctx.save();
setInterval(function () { update(); }, tpf);
}
In order to see the clipping works, I draw a different color background (blue one) in the area that I wanted clipped... the result is bad, only the first clipped area is painted blue :(
function update() {
setDirtyArea(x,y,w+1,h)
ctx.fillStyle = "rgb(0,0,128)";
ctx.fillRect (0,0,320,240);
x++;
// paint image
ctx.clearRect(x,y,w,h);
ctx.drawImage(imageObj, x, y);
}
function setDirtyArea(x,y,w,h) {
ctx.restore();
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
}
I'd love to se the blue zone propagate itself towards the right of the screen... please help, I don't understand what's wrong!
Thanks,
J.
You need to wrap the actual drawing and clipping of the box with the save and restore methods. and include the closePath method. I have modified your fiddle to work the way I believe you are trying to make it.
http://jsfiddle.net/jaredwilli/7kbzj/7/
ctx.save(); // save the context
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
ctx.restore(); // restore the context
I also have learned that using save and restore can get really complex, and confusing to know which context your in. It came up with a pretty huge canvas app im working on, and i found that indenting your canvas code helps immensely. Especially the save/restores. I have even decided it should be considered a best practice, so the more people who know and do it the better.
Hope this helps.