--- Canvas を作って container に追加 ---

view source

JavaScript

// --- Canvas を作って container に追加 ---
const container = document.getElementById('demo');
const canvas = document.createElement('canvas');
canvas.id = 'gameCanvas';
canvas.width = 1280;
canvas.height = 720;
container.appendChild(canvas);

const ctx = canvas.getContext('2d');

let frame = 0;
let x = canvas.width/2;
let y = canvas.height - 40;

// --- Man クラス定義 ---
class Man {
  constructor(x, y, size = 4) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vx = 0;
    this.vy = 0;
    this.speed = 7;
    this.gravity = 0.5;
    this.jumpPower = -18;
    this.groundY = y;
    this.isJumping = false;
    this.isCrouching = false;
    this.isPunching = false;
    this.isDancing = false;
    this.punchFrame = 0;
    this.punchDuration = 10;
    this.frame = 0;

    // 各部位(頭・胴体・腕・足)
    this.head = { x: 0, y: 0, r: 10 * size };
    this.torso = new Limb();
    this.leftArm = new Limb();
    this.rightArm = new Limb();
    this.leftLeg = new Limb();
    this.rightLeg = new Limb();
  }

  update() {
    this.x += this.vx;

    if (this.isJumping) {
      this.vy += this.gravity;
      this.y += this.vy;

      if (this.y >= this.groundY) {
        this.y = this.groundY;
        this.vy = 0;
        this.isJumping = false;
      }
    }

    if (this.isPunching) {
      this.punchFrame++;
      if (this.punchFrame >= this.punchDuration) {
        this.isPunching = false;
        this.punchFrame = 0;
      }
    }

    this.frame++;

    // 画面端制限
    this.x = Math.max(0, Math.min(canvas.width, this.x));

    // === 各部位の座標計算 ===
    const s = this.size;
    const crouchOffset = this.isCrouching ? 10 * s : 0;
    const crouchScale = this.isCrouching ? 0.7 : 1.0;

    // 頭
    this.head.x = this.x;
    this.head.y = this.y - 60 * s * crouchScale + crouchOffset;

    // 胴体(肩→腰)
    const shoulderX = this.x;
    const shoulderY = this.y - 50 * s * crouchScale + crouchOffset;
    const hipX = this.x;
    const hipY = this.y - 30 * s * crouchScale + crouchOffset;
    this.torso.set(shoulderX, shoulderY, hipX, hipY, hipX, hipY); // torsoは直線

    // 腕
    const elbowWave = this.isDancing ? Math.sin(this.frame * 0.3) * 10 * s : 0;
    this.leftArm.set(
      shoulderX, shoulderY,
      shoulderX - 10 * s, shoulderY + 10 * s + elbowWave,
      shoulderX - 20 * s, shoulderY + 20 * s + elbowWave
    );

    if (this.isPunching) {
      this.rightArm.set(
        shoulderX, shoulderY,
        shoulderX + 15 * s, shoulderY,
        shoulderX + 30 * s, shoulderY
      );
    } else {
      this.rightArm.set(
        shoulderX, shoulderY,
        shoulderX + 10 * s, shoulderY + 10 * s - elbowWave,
        shoulderX + 20 * s, shoulderY + 20 * s - elbowWave
      );
    }

    // 足
    const kneeOffset = (this.vx !== 0 || this.isDancing) ? Math.sin(this.frame * 0.3) * 5 * s : 0;
    const kneeY = this.y - 15 * s * crouchScale + crouchOffset;

    this.leftLeg.set(
      hipX, hipY,
      hipX - 8 * s, kneeY + kneeOffset,
      hipX - 10 * s, this.y
    );

    this.rightLeg.set(
      hipX, hipY,
      hipX + 8 * s, kneeY - kneeOffset,
      hipX + 10 * s, this.y
    );
  }

  draw(ctx) {
    ctx.lineWidth = 12;
    ctx.strokeStyle = "#000";
    ctx.fillStyle = "#000";

    // 頭
    ctx.beginPath();
    ctx.arc(this.head.x, this.head.y, this.head.r, 0, Math.PI * 2);
    ctx.fill();

    // 胴体
    this.torso.draw(ctx);

    // 腕
    this.leftArm.draw(ctx);
    this.rightArm.draw(ctx);

    // 足
    this.leftLeg.draw(ctx);
    this.rightLeg.draw(ctx);
  }

  jump() {
    if (!this.isJumping) {
      this.vy = this.jumpPower;
      this.isJumping = true;
    }
  }

  punch() {
    if (!this.isPunching) {
      this.isPunching = true;
      this.punchFrame = 0;
    }
  }

  crouch() {
    this.isCrouching = true;
  }

  standUp() {
    this.isCrouching = false;
  }

  dance(isDancing) {
    this.isDancing = isDancing;
  }
}




class Limb {
  constructor() {
    this.x1 = 0; this.y1 = 0; // 始点(肩・腰など)
    this.x2 = 0; this.y2 = 0; // 中間点(肘・膝)
    this.x3 = 0; this.y3 = 0; // 終点(手・足先)
  }

  set(x1, y1, x2, y2, x3, y3) {
    this.x1 = x1; this.y1 = y1;
    this.x2 = x2; this.y2 = y2;
    this.x3 = x3; this.y3 = y3;
  }

  draw(ctx) {
    ctx.beginPath();
    ctx.moveTo(this.x1, this.y1);
    ctx.lineTo(this.x2, this.y2);
    ctx.lineTo(this.x3, this.y3);
    ctx.stroke();
  }
}



// --- Manインスタンス作成 ---
const man = new Man(x, y);

// --- キーボード入力管理 ---
const keys = {
  left: false,
  right: false
};

document.addEventListener('keydown', (e) => {
  e.preventDefault();
  if (e.code === 'ArrowLeft') keys.left = true;
  if (e.code === 'ArrowRight') keys.right = true;
  if (e.code === 'Space') {
    man.jump();
  }
  if (e.code === 'KeyZ') {
    man.punch();
  }
  if (e.code === 'KeyA') {
    man.dance(true);
  }
  if (e.key === "ArrowDown") {
    man.crouch();
  }
});

document.addEventListener('keyup', (e) => {
  if (e.code === 'ArrowLeft') keys.left = false;
  if (e.code === 'ArrowRight') keys.right = false;
  if (e.key === "ArrowDown") {
    man.standUp();
  }
  if (e.code === 'KeyA') {
    man.dance(false);
  }
});

// --- メインループ ---
function loop() {
  // 入力によって速度を変える
  if (keys.left) man.vx = -man.speed;
  else if (keys.right) man.vx = man.speed;
  else man.vx = 0;

  // 描画・更新
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  man.update();
  man.draw(ctx);
  requestAnimationFrame(loop);
}

loop();

CSS

body{
  overflow:hidden;
}
canvas{
  display: block;
  margin: auto;
  width: 100vw;
  aspect-ratio: 16 / 9;
}

HTML

ページのソースを表示 : Ctrl+U , DevTools : F12

view-source:https://hi0a.com/demo/-js/js-stick-man/

ABOUT

hi0a.com 「ひまあそび」は無料で遊べるミニゲームや便利ツールを公開しています。

プログラミング言語の動作デモやWEBデザイン、ソースコード、フロントエンド等の開発者のための技術を公開しています。

必要な機能の関数をコピペ利用したり勉強に活用できます。

プログラムの動作サンプル結果は画面上部に出力表示されています。

環境:最新のブラウザ GoogleChrome / Windows / Android / iPhone 等の端末で動作確認しています。

画像素材や音素材は半分自作でフリー素材配布サイトも利用しています。LINK参照。

仕様変更、API廃止等、実験途中で動かないページもあります。