いつもんとこまで.js

だいたいprocessingとかで遊ぶ時に幾何とGUIとをいっしょくたにしてアレコレする時のスケルトン。

簡単なメインループがある。
幾何はマウスによって操作される能力を持つかもしれない。
ツールは幾何を操作する能力を持つかもしれない。
各種マネージャーはシングルトン。


<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <title>Loop Control Example</title>
  <style>
    canvas {
      display: block;
      margin: 0 auto;
      background-color: #eee;
    }

    button {
      display: block;
      margin: 10px auto;
    }
  </style>
</head>

<body>
  <canvas id="myCanvas" width="300" height="600"></canvas>
  <button id="pauseButton">Pause</button>
  <button id="resumeButton" disabled>Resume</button>
  <script>






    class InputManager{
      static instance = null;
      constructor(){
        if (InputManager.instance) {
          return InputManager.instance;
        }

        this.mouseLeftPressed = false;
        this.mouseMiddlePressed = false;
        this.mouseRightPressed = false;

        this.onMouseMove = false;

        this.dragStartRange = 5;
        this.onDrag = false;


        this.x=null;
        this.y=null;
        this.prevX=null;
        this.prevY=null;

        this.startX=null;
        this.startY=null;
        this.endX=null;
        this.endY=null;

        InputManager.instance = this;
      }

      // インスタンスの取得を管理する静的メソッド
      static getInstance() {
        if (!InputManager.instance) {
          InputManager.instance = new InputManager();
        }
        return InputManager.instance;
      }           

      mouseDown(event){
        //console.log("mousedown");
        event.preventDefault();
        this.x = event.offsetX;
        this.y = event.offsetY;        
        this.startX=event.offsetX;
        this.startY=event.offsetY;  
        this.endX=null;
        this.endY=null;

        switch (event.button) {
                case 0:
                    //console.log('Left button pressed');
                    this.mouseLeftPressed=true;
                    break;
                case 1:                  
                    //console.log('Middle button pressed');
                    this.mouseMiddlePressed=true;
                    break;
                case 2:
                    //console.log('Right button pressed');
                    this.mouseRightPressed=true;
                    break;
                case 3:
                    //console.log('Browser back button pressed');
                    break;
                case 4:
                    //console.log('Browser forward button pressed');
                    break;
                default:
                    //console.log('Unknown button pressed');
        }        
      }
      mouseMove(event){
        event.preventDefault();

        this.onMouseMove=true;
        let dx = this.x-this.startX;
        let dy = this.y-this.startY;
        if(this.mouseLeftPressed && (dx*dx)+(dy*dy)>this.dragStartRange*this.dragStartRange){
          //console.log("ok");
          this.onDrag=true;
        }

        this.prevX=this.x;
        this.prevY=this.y;
        this.x = event.offsetX;
        this.y = event.offsetY;             
      }
      mouseUp(event){
        event.preventDefault();

        this.onMouseMove=false;
        this.onDrag=false;

        this.prevX=this.x;
        this.prevY=this.y;
        this.x=null;
        this.y=null;

        this.startX=null;
        this.startY=null;
        this.endX=event.offsetX;
        this.endY=event.offsetY;

        switch (event.button) {
                case 0:
                    //console.log('Left button up');
                    this.mouseLeftPressed=false;
                    break;
                case 1:                  
                    //console.log('Middle button up');
                    this.mouseMiddlePressed=false;
                    break;
                case 2:
                    //console.log('Right button up');
                    this.mouseRightPressed=false;
                    break;
        }  
      }
      mouseClick(event){
        event.preventDefault();

      }

      keyDown(event){
        //event.preventDefault();
        //console.log("keydown");
      }
      keyUp(event){
        //event.preventDefault();

      }

      wheel(event){
        // deltaYを使用してスクロール方向を判断
        if (event.deltaY < 0) {
            //console.log('スクロール: 上');
        } else if (event.deltaY > 0) {
            //console.log('スクロール: 下');
        }
      }
    }

    class ToolManager{
      static instance = null;
      constructor(){
        if (ToolManager.instance) {
          return ToolManager.instance;
        }
        this.ToolMode = {
          SINGLE: 'SINGLE',
          SERIAL: 'SERIAL',//順次実行、ないし先頭のTOOLを実行
          PARALLEL: 'PARALLEL'//同時実行
        };
        this.mode = this.ToolMode.SINGLE;

        this.CurrentTool = new DefaultTool(this);
        this.CurrentTools = [];

        ToolManager.instance = this;
      }

      // インスタンスの取得を管理する静的メソッド
      static getInstance() {
        if (!ToolManager.instance) {
          ToolManager.instance = new ToolManager();
        }
        return ToolManager.instance;
      }         

      update(){
        // if(this.mode==="SINGLE"){
        //   if(this.CurrentTool){
        //     if(this.CurrentTool.update){
        //       this.CurrentTool.update();
        //     }
        //   }
        // }
        if(this.mode === "SINGLE" && this.CurrentTool?.update){
          this.CurrentTool.update();
        }
        else if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
          for (const tool of this.CurrentTools) {
            if (tool.update) {
              tool.update();
            }
          }
        }

        //関数抽出記法
        // function updateTool(tool) {
        //   if (tool && tool.update) {
        //     tool.update();
        //   }
        // }
        // // PARALLELモードでのツールの更新
        // if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
        //   this.CurrentTools.forEach(updateTool);
        // }        
      }

      //そのToolなりの表示能力がある場合
      draw(ctx){
        if(this.mode === "SINGLE" && this.CurrentTool?.draw){
          this.CurrentTool.draw(ctx);
        }
        else if (this.mode === "PARALLEL" && Array.isArray(this.CurrentTools)) {
          for (const tool of this.CurrentTools) {
            if (tool.draw) {
              tool.draw(ctx);
            }
          }
        }
      }

    }

    class ObjectManager{
      static instance = null;
      constructor(){
        if (ObjectManager.instance) {
          return ObjectManager.instance;
        }
        this.Objects = [];
        ObjectManager.instance = this;
      }

      // インスタンスの取得を管理する静的メソッド
      static getInstance() {
        if (!ObjectManager.instance) {
          ObjectManager.instance = new ObjectManager();
        }
        return ObjectManager.instance;
      }      

      //自動で動く場合
      update(){
        for(const obj of this.Objects){
          if(obj?.update){
            obj.update();
          }
        }
      }
      //基本的に表示能力は持つ
      draw(ctx){
        for(const obj of this.Objects){
          if(obj?.draw){
            obj.draw(ctx);
          }
        }
      }
    }

    class DefaultTool{
      constructor(toolManager){
        this.ToolManager=toolManager;
        this.Target = null;
      }

      update(){
        //console.log(inputManager.x);
        
        if(inputManager.x===null||inputManager.y===null){return;}
        
        //console.log("ok");

        if(inputManager.onDrag&&this.Target!==null){
          this.Target.x=inputManager.x;
          this.Target.y=inputManager.y;
        }else if(inputManager.mouseLeftPressed){          
          for (const obj of objectManager?.Objects || []) {
            if (obj instanceof Dot) {
              let dx = inputManager.x - obj.x;
              let dy = inputManager.y - obj.y;

              // console.log("x:"+inputManager.x+"-"+"obj.x"+obj.x+"="+"dx:"+dx);
              // console.log("dy"+dy);
              // console.log("r"+obj.r);
              //console.log("ok");
              if ((dx*dx) + (dy*dy) < obj.r * obj.r) {
                //console.log("ok");
                this.Target = obj;
              }
            }
          }
        }else if(!inputManager.mouseLeftPressed){
          this.Target=null;
        }
      }

      draw(){

      }
    }


    class Dot {
      constructor(x, y, r) {
        this.x = x;
        this.y = y;
        this.r = r;
      }
      update() {
      }
      draw(ctx) {        
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
        ctx.stroke();                
      }
    }


    let inputManager = null;
    let toolManager = null;
    let objectManager = null;

    class App {

      constructor() {
        this.canvas = document.getElementById("myCanvas");
        this.ctx = this.canvas.getContext("2d");
        this.lastFrameTime = 0;
        this.targetFrameDuration = 1000 / 60; // 60FPS
        this.animationFrameId = null;
        this.isPaused = false;


        this.inputManager = new InputManager();
        inputManager=this.inputManager;
        this.canvas.addEventListener('mousedown', this.inputManager.mouseDown.bind(this.inputManager));
        this.canvas.addEventListener('mousemove', this.inputManager.mouseMove.bind(this.inputManager));
        this.canvas.addEventListener('mouseup', this.inputManager.mouseUp.bind(this.inputManager));

        window.addEventListener('wheel', this.inputManager.wheel.bind(this.inputManager));
        //Canvas内で右クリックメニューが出ないようにする
        this.canvas.addEventListener('contextmenu', function (event) {
          event.preventDefault();
        });

        document.addEventListener('keydown', this.inputManager.keyDown.bind(this.inputManager));
        document.addEventListener('keyup', this.inputManager.keyUp.bind(this.inputManager));

        this.toolManager = new ToolManager();
        toolManager=this.toolManager;
        this.objectManager=new ObjectManager();
        objectManager=this.objectManager;

        let dot = new Dot(100, 100, 100);
        this.objectManager.Objects.push(dot);


      };
      

      start() {
        this.gameLoop(performance.now());
      }

      gameLoop(currentTime) {
        if (this.isPaused) return;
        const deltaTime = currentTime - this.lastFrameTime;
        if (deltaTime >= this.targetFrameDuration) {

          //inputはInputManagerがやる

          this.update(deltaTime);
          // Check if update took too long
          if (performance.now() - currentTime < this.targetFrameDuration) {
            this.render();
          }
          this.lastFrameTime = currentTime;
        }
        //this.animationFrameId = requestAnimationFrame(this.gameLoop);
        this.animationFrameId = requestAnimationFrame(() => this.gameLoop(performance.now()));
      }

      update(deltaTime) {
        // Update logic here
        //console.log(this.inputManager.x);

        this.toolManager.update();
        this.objectManager.update();
      }

      render() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        // Rendering logic here

        //this.dot.draw(this.ctx);
        this.toolManager.draw(this.ctx);
        this.objectManager.draw(this.ctx);
      }
    }//App

    function init() {
      
      document.getElementById("pauseButton").onclick = function () {
        app.isPaused = true;
        if (app.animationFrameId) {
          cancelAnimationFrame(app.animationFrameId);
          app.animationFrameId = null;
        }
        this.disabled = true;
        document.getElementById("resumeButton").disabled = false;
      };

      document.getElementById("resumeButton").onclick = function () {
        if (!app.isPaused) return;
        app.isPaused = false;
        app.lastFrameTime = performance.now();
        app.gameLoop(app.lastFrameTime);
        this.disabled = true;
        document.getElementById("pauseButton").disabled = false;
      };

      const app = new App();
      app.start();

    }//init

    document.addEventListener('DOMContentLoaded', function () {
      init();
    })
  </script>
</body>
</html>

この記事が気に入ったらサポートをしてみませんか?