首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用于自上而下RPG游戏的Javascript画布视图端口摄像机

用于自上而下RPG游戏的Javascript画布视图端口摄像机
EN

Stack Overflow用户
提问于 2022-07-16 10:53:52
回答 1查看 199关注 0票数 0

编辑:我的问题到底是什么?我想要创建一个“视口”相机效果,它将跟随玩家而不移动背景。

我正在添加一个websocket支持,并将在地图上呈现更多的字符--我需要根据玩家而不是地图进行移动,这样我就可以正确地更新其他球员的移动位置。

原始员额:

关于这个问题,我已经翻阅了大多数其他的帖子。似乎,每个人都有自己独特的问题,为自己独特的实现“画布自上而下的游戏”。这使得在不重构整个代码的情况下很难找到解决问题的方法。

希望你们这次能帮我。

简单的游戏(到目前为止)-使用Vue3顺便,但这是除了重点-大部分的代码只是香草的东西。

简单的自顶向下的地图。X和Y轴

我在我的实现中所做的是使用我用平铺应用程序创建的PNG

放大约400% -用ctx.drawImage绘制它

我也在设定界限

然后画玩家

我现在要做的是在按下键的x和y位置上添加一个“强制”值,这意味着字符在地图上移动,碰撞检测起作用,我想要的是地图根据字符的位置绘制x个像素数量,而不移动地图,因为我已经实现了一些SocketIO代码来实现这个多人游戏。

我真的很迷茫,不知道我该怎么做才能把地图画成一个视图.

我希望这其中的一些有意义

一些代码

代码语言:javascript
复制
function create() {
        const canvas: HTMLCanvasElement = document.getElementById("game") as HTMLCanvasElement;
        if (!canvas) return console.error("canvas is undefined");
        const ctx: CanvasRenderingContext2D = canvas.getContext("2d") as CanvasRenderingContext2D;
        ctx.imageSmoothingEnabled = false;
        initializeGameWindow(canvas, ctx);
        setEvents(canvas, ctx);
        const playerSpriteWidth = 192;
        const playerSpriteHeight = 68;
        const spriteFrames = 4;
        background = new Sprite();
        player = new Sprite();
        update(canvas, ctx);
    }

    function update(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
        window.requestAnimationFrame(() => update(canvas, ctx));
        drawGame(canvas, ctx);
    }

    function drawGame(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) {
        background.draw(ctx);
        boundaries.forEach((boundary) => {
            boundary.draw(ctx);
        });
        player.value.draw(ctx);
        let moving: boolean = true;
        player.value.moving = false;
        if (keys.w.pressed && lastKey.value === "w") {
            player.value.moving = true;
            player.value.image = player.value.sprites.up;
            for (let i = 0; i < boundaries.length; i++) {
                if (
                    rectengularCollision(player.value, {
                        ...boundaries[i],
                        position: { x: boundaries[i].position.x, y: boundaries[i].position.y + Sprite.force },
                    })
                ) {
                    moving = false;
                    break;
                }
            }
            if (!moving) return;
            player.value.position.y -= Sprite.force;
            
        } else if (keys.a.pressed && lastKey.value === "a") {
            player.value.moving = true;
            player.value.image = player.value.sprites.left;
            for (let i = 0; i < boundaries.length; i++) {
                if (
                    rectengularCollision(player.value, {
                        ...boundaries[i],
                        position: { x: boundaries[i].position.x + Sprite.force, y: boundaries[i].position.y },
                    })
                ) {
                    moving = false;
                    break;
                }
            }
            if (!moving) return;
            player.value.position.x -= Sprite.force;
            
        } else if (keys.s.pressed && lastKey.value === "s") {
            player.value.moving = true;
            player.value.image = player.value.sprites.down;
            for (let i = 0; i < boundaries.length; i++) {
                if (
                    rectengularCollision(player.value, {
                        ...boundaries[i],
                        position: { x: boundaries[i].position.x, y: boundaries[i].position.y - Sprite.force },
                    })
                ) {
                    moving = false;
                    break;
                }
            }
            if (!moving) return;
            player.value.position.y += Sprite.force;
            
        } else if (keys.d.pressed && lastKey.value === "d") {
            player.value.moving = true;
            player.value.image = player.value.sprites.right;
            for (let i = 0; i < boundaries.length; i++) {
                if (
                    rectengularCollision(player.value, {
                        ...boundaries[i],
                        position: { x: boundaries[i].position.x - Sprite.force, y: boundaries[i].position.y },
                    })
                ) {
                    moving = false;
                    break;
                }
            }
            if (!moving) return;
            player.value.position.x += Sprite.force;
            
        }
    }
    
    export class Sprite {
        static force: number = 3; // speed, velocity, acceleration, etc.
        frames: number;
        spriteIteration: number = 0;
        elapsed: number = 0;
        defaultSrc: string;
        image: HTMLImageElement;
        sprites: { up: HTMLImageElement; down: HTMLImageElement; left: HTMLImageElement;             
        right: HTMLImageElement };
        position: { x: number; y: number };
        width: number = 0;
        height: number = 0;
        moving: boolean = false;
        constructor(
            position: { x: number; y: number },
            src : string,
            frames: number = 1,
            sprites: { up: string; down: string; left: string; right: string } = { up:           
            "", down: "", left: "", right: "" },
            ) {
                this.defaultSrc = src;
                this.moving = false;
                const { up, down, left, right } = this.initSprites(sprites);
                this.sprites = { up, down, left, right };
                this.image = down;
                this.frames = frames;
                this.position = position;
                    this.image.onload = () => {
                    this.width = this.image.width / this.frames;
                    this.height = this.image.height;
                };
        }

    draw(ctx: CanvasRenderingContext2D) {
        ctx.drawImage(
            // src
            this.image,
            // crop from x axis
            this.spriteIteration * this.width,
            // crop from y axis
            0,
            // crop width 
            this.image.width / this.frames,
            // crop height
            this.image.height,
            // x position on canvas
            this.position.x,
            // y position on canvas
            this.position.y,
            // width on canvas
            this.image.width / this.frames,
            //  height on canvas
            this.image.height,
        );
    }

    initSprites(sprites: { up: string; down: string; left: string; right: string }) {
        const up = new Image();
        up.src = sprites.up;
        const down = new Image();
        down.src = sprites.down !== "" ? sprites.down : this.defaultSrc;
        const left = new Image();
        left.src = sprites.left;
        const right = new Image();
        right.src = sprites.right;
        return { up, down, left, right };
    }
}
EN

回答 1

Stack Overflow用户

发布于 2022-07-18 13:07:45

由于您还没有为我提供一个可运行的代码片段供我使用,下面是一个非常通用的示例,演示如何使用CanvasRenderingContext2D的转换方法更改场景的视口。

draw中,您将看到:

使用setTransform

  • To从播放机的角度重新设置转换以使用<canvas>作为(0, 0)的中心:
  • 将上下文按-x-y乘以缩放因子

H 114缩放factor

缩放画布

代码语言:javascript
复制
// Initialize
const cvs = document.createElement("canvas");
cvs.width = 200;
cvs.height = 200;
const ctx = cvs.getContext("2d");

// State
const players = [
  [-20, -30, "green"],
  [50, 70, "blue"]
];

let viewport = {
  x: 0,
  y: 0,
  zoom: 1
};

// Drawing methods
const clear = () => {
  ctx.fillStyle = "white";
  ctx.fillRect(-cvs.width / 2, -cvs.height / 2, cvs.width, cvs.height);
};

const drawMap = () => {
  const w = cvs.width - 25;
  const h = cvs.height - 25;
  
  ctx.fillStyle = "#efefef";
  ctx.fillRect(
   -w / 2,
   -h / 2,
   w,
   h
  );
}

const drawPlayers = () => {
  for (const [x, y, color] of players) {
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(x, y, 10, Math.PI * 2, 0);
    ctx.closePath();
    ctx.fill();
  }
}

const drawCenter = () => {
  ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
  ctx.fillRect(-1, -15, 2, 30);
  ctx.fillRect(-15, -1, 30, 2);
}

const draw = () => {
  // Zoom out and set (0, 0) to center of canvas
  ctx.setTransform(1, 0, 0, 1, cvs.width / 2, cvs.height / 2);
  clear();
  
  const { zoom, x, y } = viewport;
  ctx.translate(-x * zoom, -y * zoom);
  ctx.scale(zoom, zoom);
  
  drawMap();
  drawPlayers();
  
  // For debugging: mark the center of the canvas
  ctx.setTransform(1, 0, 0, 1, cvs.width / 2, cvs.height / 2);
  drawCenter();
}

draw();

document.body.appendChild(cvs);

// UI
const updateViewport = () => {
  const centerValue = document.querySelector("input[name=center]:checked").value;
  
  const [x, y] = players[centerValue] || [0, 0];
  const zoom = document.querySelector("input[type=range]").valueAsNumber;
  
  viewport = { x: x, y: y, zoom };
  draw();
};

document.querySelectorAll("input").forEach(el => {
  el.addEventListener("input", updateViewport);
});
代码语言:javascript
复制
body { display: flex; }
canvas { border: 1px solid red; position: relative; }
fieldset { margin-bottom: 1rem; }
代码语言:javascript
复制
<div> 
  <fieldset>
    <legend>View center:</legend>
    <label>
      <input type="radio" value="-1" name="center" checked> Map
    </label>

    <label>
      <input type="radio" value="0" name="center"> Green player
    </label>

    <label>
      <input type="radio" value="1" name="center"> Blue player
    </label>
  </fieldset>
  
  <fieldset>
    <legend>Zoom</legend> 
    <input type="range" min="0.1" max ="3" step="0.1" value="1">
  </fieldset>
  
</div>

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73003572

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档