0

I have created some Javascript code that draws a square on the screen and animates it upwards perpetually. What I want is for the square to stay in the center of the screen while the world (canvas) around it moves downwards giving the illusion that the square is moving infinitely upwards. The problem is, I have no idea of how to do this, any help would be greatly appreciated!

My Code

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var rectWidth = 50;
var rectHeight = 50;
var rectRad = 25;
var x = (canvas.width/2)-rectRad;
var y = canvas.height-rectHeight;
var dx = 2;
var dy = 4;
var ch = canvas.height;

function rect() {
 ctx.beginPath();
 ctx.rect(x, y, rectWidth, rectHeight);
 ctx.fillStyle = "#0095DD";
 ctx.fill();
 ctx.closePath();
}

function draw() {
 ctx.clearRect(0, 0, canvas.width, canvas.height);
 rect();

 if(y + dy < 0) {
  ch += 4;
 }

 if(y + dy > canvas.height-rectHeight) {
  dy = -dy;
 }

 y += dy;
}
setInterval(draw, 10);
* { padding: 0; margin: 0; }
  canvas { background: #eee; display: block; margin: 0 auto; }
<!DOCTYPE html>

<html>

<head>
 <meta charset="utf-8" />
 <title>Game</title>
 
 <style>
  * { padding: 0; margin: 0; }
  canvas { background: #eee; display: block; margin: 0 auto; }
 </style>

</head>
<body>

<canvas id="myCanvas" width="480" height="320"></canvas>

<script src="gameJS.js"></script>

</body>

</html>
Gavin T
  • 51
  • 1
  • 8

1 Answers1

0

You just need to draw the desiged background and translate the backgrounds y position by your the speed you want to have.

This is mostly called a camera. The camera handles the translation of the x and y positions while the square (or the player entity) is in the middle of the screen.

You also need to make sure that your background (image, color, pattern) is allways visible by the viewport.

I'll give you an example camera i made for a test game engine written in typescript to translate the coordinates.

/**
 * calculates the position offset for camera following
 */
private static calculatePositionOffsetForCameraFollow(originalPosition: Vector2D, camera: Camera): Vector2D {

    // first check if there is any following
    let entity = camera.getFollowingEntity();

    // if no following is active, return the original position
    if (!entity) return originalPosition;

    // calculate the offset. the entity should be in the center of the screen
    let canvasDim = CameraOffsetCalculator.getCanvasDimension();

    // calculate the center position for the entity and shift the other
    // vectors.
    let tmpVector = originalPosition.substract(entity.getPosition()).add(
        canvasDim.divide(new Vector2D(2, 2))
    );

    // now check if the camera has world bounds
    let worldBounds = camera.getWorldBounds();
    if (!worldBounds) return tmpVector;

    // check if the original vector is smaller than the shifted vector
    // this will bound the left and top world bounds
    if (originalPosition.x < tmpVector.x) {

        // reset the x axis to fix the camera
        tmpVector.x = originalPosition.x;
    }

    if (originalPosition.y < tmpVector.y) {

        // reset the y axis to fix the camera
        tmpVector.y = originalPosition.y;
    }

    // now the left and bottom bounds
    // we need the world dimension to check if the camera reaches
    // the end of the world in the visible area
    let entityPosition = entity.getPosition();
    let worldBoundCanvas = worldBounds.substract(canvasDim.half());

    if (entityPosition.x > worldBoundCanvas.x) {

        // get the original position and substract
        // the distance from world bounds and canvas dim
        tmpVector.x = originalPosition.x - (worldBounds.x - canvasDim.x);
    }

    if (entityPosition.y > worldBoundCanvas.y) {

        // get the original position and substract
        // the distance from world bounds and canvas dim
        tmpVector.y = originalPosition.y - (worldBounds.y - canvasDim.y);
    }

    // return the corrected position vector
    return tmpVector;
}

This function returns a new point for the background image or tile to draw at while following an entity (in your case the square).

Let me know if this has helped you!

Here is the illusion integrated in your code with a very simplified camera:

    var canvas = document.getElementById("myCanvas");
    var ctx = canvas.getContext("2d");
    var rectWidth = 50;
    var rectHeight = 50;
    var rectRad = 25;
    var x = (canvas.width / 2) - rectRad;
    var y = (canvas.height / 2) - rectHeight;
    //var dx = 2;
    //var dy = 4;
    var ch = canvas.height;

    // should be lower than bgHeight
    var scrollSpeed = .0475;

    // a boolean to make the background shifts differently at
    // y position to make the user think the player entity is moving
    // forwards
    var scroller = 0;

    // the repeating background
    var bg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAQCAMAAAA7+k+nAAAAKlBMVEXx8fH7+/v8/Pz5+fn29vb39/f19fXz8/P4+Pj6+vr09PTy8vL////o6OgghGlxAAAAk0lEQVR42nWPSQ7DMAwDZWujxPj/363dS1qk1YkCwQFG1p+Tdd33mXcxIppoSa4r0R6ZAHfR1kUwI9ZVLBpnu8p+nEGWY67LfXZijDyLgHmd2UEd7Hu2Czc4kE7d2MyATqk6qMgaraq984SWear99/g2uLMEaOTTRmSMgD1tRNsQ82kjnuX6w0ZazeSHjdAsE0+bFzAoDXEZIyZUAAAAAElFTkSuQmCC";
    var bgBitmap = createImageBitmap(b64ToBlob(bg, "image/png"));

    function rect() {
        ctx.beginPath();
        ctx.rect(x, y, rectWidth, rectHeight);
        ctx.fillStyle = "#0095DD";
        ctx.fill();
        ctx.closePath();
    }

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // first draw the background
        drawBackground();

        // and now the rectangle
        rect();
    }

    function drawBackground() {

        var bgWidth = 14, bgHeight = 14;

        // draw the full visible screen and on each edge, draw another tile
        // to make a smooth scrolling
        for (var x = -1; x < canvas.width / bgWidth; x++) {
            for (var y = -1; y < canvas.height / bgHeight; y++) {

                var yCorrectedCoordinate = y + (scroller * scrollSpeed);

                // draw!
                ctx.drawImage(
                    bgBitmap, x * bgWidth, yCorrectedCoordinate * bgHeight,
                    bgWidth, bgHeight
                );
            }
        }

        // change the scroller offset
        scroller++;
        if (scroller > 20)
            scroller = 0;
    }

    /**
    * converts a base64 string to a blob image capable for using in canvas environment
    * @see https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
    */
    function b64ToBlob(b64Data, contentType, sliceSize) {

        contentType = contentType || '';
        sliceSize = sliceSize || 512;

        var byteCharacters = atob(b64Data.split(',')[1]);
        var byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            var slice = byteCharacters.slice(offset, offset + sliceSize);

            var byteNumbers = new Array(slice.length);
            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            var byteArray = new Uint8Array(byteNumbers);

            byteArrays.push(byteArray);
        }

        var blob = new Blob(byteArrays, { type: contentType });
        return blob;
    }

    // start the game loop after the image has beed loaded
    bgBitmap.then(function (bg) {

        bgBitmap = bg;
        setInterval(draw, 10);
    });

    // i would recommend to use window.requestAnimationFrame() instead

If there are other questions about game development, here is my repository: qhun-engine at Github.com

wartoshika
  • 531
  • 3
  • 10