/**
* システム初期パラメータ(表示対象のキャンバスと解像度の指定)
* @typedef {object} GameCoreSysParam システム初期設定用パラメータ
* @property {string} canvasId canvasDOM Id
* @property {number} screen[].resolution.w screen width pixelsize
* @property {number} screen[].resolution.h screen height pixelsize
* @property {number} screen[].resolution.x screen offset x
* @property {number} screen[].resolution.y screen offset y
* @example
const GameCoreSysParam = {
CanvasId:"canvas",
screen: [
{resolution: {w:1024, h:768, x:0, y:0}},
{resolution: {w:1024, h:768, x:0, y:0}}
]
}
* @description
* -複数のScreenを設定可能、
* -対象指定時、順番にgame.screen[0], game.screen[1] ... となる
* -定数値にして名前を付けた方が管理が判りやすくなります
*
*/
/**
* ゲームエンジン本体
* @summary ゲームエンジン本体/インスタンス化して実行
* 実行時のエントリーポイント
* @param {GameCoreSysParam} sysParam システム初期設定パラメータ
* @example
* //宣言:
* const game = new GameCore( sysParam );
* //game.screen[0]のパラメータが実際の使用Canvasの解像度となる。
* //offsetパラメータはscreen[0]を基準位置としての表示位置offset。(screen[1以降]で有効)
* //ScreenはoffscreenBufferとして処理され、指定した解像度で作成される。
*
* //ゲームループの開始:
* game.run();
* //requestAnimationFrameの周期毎にタスクを実行する。
*
* @description
* ゲームエンジンの主要なインスタンスであり、実行時のエントリーポイントです。.<br>\
* ゲームタスク、アセット管理、描画レイヤー、入力デバイス、<br>\
* サウンドシステムなど、全てのコア機能を統合し制御します。
*/
class GameCore {
/**
* @param {GameCoreSysParam} sysParam システム初期設定パラメータ
* @description
* GameCoreインスタンスを初期化し、ゲームの基盤となるコンポーネントを設定します。<br>\
* 入力デバイス、サウンド、描画システム、アセットマネージャーなどを生成し、<br>\
* メインループが動作するための準備を整えます。
*/
constructor(sysParam) {
/**
* FPSや負荷の計測用(内部関数)
* @class GameCore.bench
* @classdesc
* ゲームのFPS(フレームレート)とワークロード(処理負荷)を計測するユーティリティです。<br>\
* フレームごとの時間間隔と処理時間を記録し<br>\
* 平均、最大、最小値として結果を提供します。
*/
class bench {
/**
*
*/
constructor() {
let oldtime; let newtime; // = Date.now();
let cnt = 0;
let fps_log = []; let load_log = [];
let log_cnt = 0;
let log_max = 0;
let workload; let interval;
let fps = 0;
let dt = 0;
let ot = 0;
let blinkCounter = 0;
const BLINK_ITVL = 1500;
const BLINK_TIME = 500;
//let ypos = 412;
/**
* 負荷計測区間開始指示
* @method
* @description
* 処理負荷計測区間の開始を指示します。<br>\
* このメソッドが呼ばれた時点のパフォーマンスタイムスタンプを記録し、<br>\
* 次の`end`メソッドまでの時間を測定します。
*/
this.start = function () {
oldtime = newtime;
newtime = performance.now(); //Date.now();
};
/**
* 負荷計測区間終了指示
* @method
* @description
* 処理負荷計測区間の終了を指示し、計測データを記録します。<br>\
* `start`からの処理時間(ワークロード)とフレーム間隔を計算し、<br>\
* パフォーマンスログに追加します。
*/
this.end = function () {
workload = performance.now() - newtime; //Date.now() - newtime;
interval = newtime - oldtime;
if (log_cnt > log_max) log_max = log_cnt;
fps_log[log_cnt] = interval;
load_log[log_cnt] = workload;
log_cnt++;
if (log_cnt > 59) log_cnt = 0;
let w = 0;
for (let i = 0; i <= log_max; i++) {
w += fps_log[i];
}
cnt++;
//fps = parseInt(1000 / (w / (log_max + 1)));
fps = 1000 / (w / (log_max + 1));
};
/**
* @typedef {object} resultLog 計測結果
* @property {number} fps FPS
* @property {number} logpointer ログ配列の最新更新値へのインデックス値
* @property {number[]} interval.log フレーム時間ログ
* @property {number} interval.max 最大値
* @property {number} interval.min 最小値
* @property {number} interval.ave 平均値
* @property {number[]} workload.log 負荷ログ
* @property {number} workload.max 最大値
* @property {number} workload.min 最小値
* @property {number} workload.ave 平均値
*/
/**
* @method
* @return {resultLog} 計測結果(.interval .workload)
* @description
* FPSとワークロードの計測結果をオブジェクトで返します。<br>\
* 各ログの平均、最大、最小値を含む詳細なパフォーマンスデータを提供し、<br>\
* ゲームの最適化に役立ちます。
*/
this.result = function () {
let int_max = 0;
let int_min = 999;
let int_ave = 0;
let load_max = 0;
let load_min = 999;
let load_ave = 0;
let wlod = 0;
let wint = 0;
for (let i = 0; i <= log_max; i++) {
//fstr += fps_log[i] + " ";
//lstr += load_log[i] + " ";
if (int_max < fps_log[i]) int_max = fps_log[i];
if (int_min > fps_log[i]) int_min = fps_log[i];
if (load_max < load_log[i]) load_max = load_log[i];
if (load_min > load_log[i]) load_min = load_log[i];
wlod += load_log[i];
wint += fps_log[i];
}
//int_ave = parseInt(wint / (log_max + 1));
//load_ave = parseInt(wlod / (log_max + 1));
int_ave = wint / (log_max + 1);
load_ave = wlod / (log_max + 1);
let r = {};
r.fps = fps;
r.logpointer = log_cnt;
let wl = {};
wl.log = load_log;
wl.max = load_max;
wl.min = load_min;
wl.ave = load_ave;
let iv = {};
iv.log = fps_log;
iv.max = int_max;
iv.min = int_min;
iv.ave = int_ave;
r.interval = iv;
r.workload = wl;
return r;
};
/**
* blink用の基準時間を呼び出し元からもらう
* @method
* @param {number} t now time(ms)
* @description
* 点滅(blink)機能の基準となる現在のシステム時刻を受け取ります。<br>\
* この時刻情報を使って、点滅カウンターを更新し<br>\
* 点滅状態の判定に利用します。
*/
this.setTime = function (t) {
ot = dt;
dt = t;
blinkCounter = blinkCounter + (dt - ot);
if (blinkCounter > BLINK_ITVL) blinkCounter = 0.0;
};
/**
* @method
* @return {number} 1フレームの時間を返す(ms)
* @description
* 直前のフレームに要した時間(デルタタイム)をミリ秒単位で返します。<br>\
* この値は、フレームレートが変動する環境での<br>\
* ゲームロジックの調整に利用できます。
*/
this.readTime = function () {
return dt - ot; //deltaTimeを返す(ms) 実績 Chrome PC:float/iOS,iPadOS:Integer
};
/**
* @method
* @return {number} エンジンが起動してからの経過時間を返す(ms)
* @description
* ゲームエンジンが起動してからの経過時間(ライフタイム)をミリ秒単位で返します。<br>\
* これは、ゲーム全体を通じた時間の管理や <br>\
* 特定のイベントのタイミング制御に利用できます。
*/
this.nowTime = function () {
return dt; //lifeTimeを返す(ms)
};
/**
* @method
* @return {boolean} 一定間隔(1.5s/0.5s)でtrue/falseを返す
* @description
* 一定の間隔(1.5秒`true`、0.5秒`false`)で`true`/`false`を繰り返すブール値を返します。<br>\
* UI要素の点滅表示や、周期的なイベントのトリガーなど <br>\
* 視覚的な合図や時間制御に利用できます。
*/
this.blink = function () {
//return blinkCounter + ":" + BLINK_ITVL + ":" + dt;//(parseInt(blinkCounter) < BLINK_TIME)?true:false;
return (blinkCounter < BLINK_TIME) ? true : false;
};
}
}
let runStatus_ = false;
const task_ = new GameTaskControl(this);
//device setup
const keyboard_ = new inputKeyboard();
const mouse_ = new inputMouse(sysParam.canvasId);
const joystick_ = new inputGamepad();
const touchpad_ = new inputTouchPad(sysParam.canvasId);
const vGpad_ = new inputVirtualPad(mouse_, touchpad_);
const beep_ = new Beepcore();
const screen_ = [];
const w = sysParam.screen[0].resolution.w;
const h = sysParam.screen[0].resolution.h;
const canvas = document.getElementById(sysParam.canvasId);
canvas.width = w; canvas.height = h;
this.systemCanvas = canvas;
const ctx = canvas.getContext("2d");
for (let i in sysParam.screen) {
let wsysp = sysParam.screen[i];
screen_[i] = new DisplayControl(ctx,
wsysp.resolution.w, wsysp.resolution.h,
wsysp.resolution.x, wsysp.resolution.y,
sysParam.offscreen
);
}
const viewport_ = new viewport();
viewport_.size(w, h); viewport_.border(w / 2, h / 2);
viewport_.setPos(0, 0); viewport_.repeat(false);
//
const sprite_ = new GameSpriteControl(this);
//
const font_ = [];
this.setSpFont = function (fontParam) {
let fprm = {
Image: asset_.image[fontParam.id].img,
pattern: fontParam.pattern,
ucc: fontParam.ucc
};
let wf = new GameSpriteFontControl(this, fprm);
font_[fontParam.name] = wf;
};
//assetsetup
const asset_ = new GameAssetManager();
// soundはassetを参照するので↑の後で宣言する。
const sound_ = new soundControl(asset_);
// mainloop
let sysp_cnt = sysParam.screen.length;
const tc = new bench();
let sintcnt = []; //screenIntervalCounter
for (let i = 0; i < sysp_cnt; i++) sintcnt[i] = 0;
/**
* game main roop (requestAnimationFrame callback function)
* @param {number} t calltime/performance.now()
* @description
* ゲームのメインループとして機能する`requestAnimationFrame`のコールバック関数です。<br>\
* 毎フレーム、タスクの更新、ビープ音の再生、描画バッファのクリア、<br>\
* スプライトの描画、最終的な画面反映といった一連の処理を実行します。
*/
function loop(t) {
if (runStatus_) {
tc.setTime(t);
tc.start();
task_.step();
beep_.step(t); //beep play再生用
for (let i = 0; i < sysp_cnt; i++) {
if (screen_[i].getInterval() - sintcnt[i] == 1) {
screen_[i].reflash();
screen_[i].clear();
//これで表示Bufferがクリアされ、先頭に全画面消去が登録される。
}
}
task_.draw();
sprite_.allDrawSprite(); //スプライトをBufferに反映する。
for (let i = 0; i < sysp_cnt; i++) {
screen_[i].draw();
//これで全画面がCanvasに反映される。
}
tc.end();
for (let i = 0; i < sysp_cnt; i++) {
sintcnt[i]++;
if (sintcnt[i] >= screen_[i].getInterval()) sintcnt[i] = 0;
}
//run
requestAnimationFrame(loop); //"use strict"対応
} else {
//pause
}
}
//public propaty and method
/**
* @type {GameTaskControl}
*/
this.task = task_;
/**
* @type {GameAssetManager}
*/
this.asset = asset_;
/**
* @type {inputKeyboard}
*/
this.keyboard = keyboard_;
/**
* @type {inputMouse}
*/
this.mouse = mouse_;
/**
* @type {inputGamepad}
*/
this.gamepad = joystick_;
/**
* @type {inputGamepad}
*/
this.joystick = joystick_;
/**
* @type {inputTouchPad}
*/
this.touchpad = touchpad_;
/**
* @type {inputVirtualPad}
*/
this.vgamepad = vGpad_;
/**
* @type {DisplayControl[]}
*/
this.dsp = screen_[0];
/**
* @type {DisplayControl[]}
*/
this.screen = screen_;
/**
* @type {viewport}
*/
this.viewport = viewport_;
/**
* @type {soundControl}
*/
this.sound = sound_;
/**
* @type {Beepcore}
*/
this.beep = beep_;
//
/**
* @type {GameSpriteControl}
*/
this.sprite = sprite_;
/**
* @type {GameSpriteFontControl}
*/
this.font = font_;
this.state = {};
/**
* FPS/workload count Utility
* @type {bench}
*/
this.fpsload = tc;
/**
* @method
* @return {number} 1フレームの時間を返す(ms)
* @description
* 直前のフレームに要した時間(デルタタイム)をミリ秒単位で返します。<br>\
* この値は、フレームレートが変動する環境での<br>\
* ゲームロジックの調整に利用できます。
*/
this.deltaTime = tc.readTime; //
/**
* @method
* @return {number} エンジンが起動してからの経過時間を返す(ms)
* @description
* ゲームエンジンが起動してからの経過時間(ライフタイム)をミリ秒単位で返します。<br>\
* これは、ゲーム全体を通じた時間の管理や <br>\
* 特定のイベントのタイミング制御に利用できます。
*/
this.time = tc.nowTime; //
/**
* @method
* @return {boolean} 一定間隔(1.5s/0.5s)でtrue/falseを返す
* @description
* 一定の間隔(1.5秒`true`、0.5秒`false`)で`true`/`false`を繰り返すブール値を返します。<br>\
* UI要素の点滅表示や、周期的なイベントのトリガーなど <br>\
* 視覚的な合図や時間制御に利用できます。
*/
this.blink = tc.blink; //function return bool
// init
sprite_.useScreen(0);
/**
* ゲームループの開始
* requestAnimationFrameの周期毎にタスクを実行する。
* @method
* @description
* ゲームループの実行を開始します。<br>\
* `requestAnimationFrame`を介して`loop`関数が周期的に呼び出され<br>\
* ゲームの全ての処理が動き始めます。
*/
this.run = function () {
runStatus_ = true;
requestAnimationFrame(loop);
};
/**
* ゲームループの停止
* @method
* @description
* ゲームループの実行を一時停止します。<br>\
* これにより`requestAnimationFrame`の呼び出しが止まり、<br>\
* ゲームの更新や描画が中断されます。
*/
this.pause = function () {
runStatus_ = false;
};
}
}