図形クラスを整備したい
ゲームを作る時、キャンバスに描画すする図形を扱ったり、複数の図形が接触しているか否かを判定したりなどの処理が必要になるはずです。しかし、TypeScriptには図形を取り扱うクラスやインターフェースがありません。
そういった、図形を扱うようなクラスや関数を整備したいと考えました。
まず基本的な図形インターフェースを整備する
図形の接触判定などを少し難しそうなので、まずは基本的な図形クラスを整備します。漠然と考えているイメージとしては、C#におけるPoint構造体やRectangle構造体のようなものを整備したいと考えました。
TypeScriptには構造体はないので、クラスまたはインターフェースという形で作っていきます。
「TypeScriptでゲームプログラム15」では、四角形を表すクラスRectと円を表すクラスCircleを作っていました。そしてRectはラケット、Circleは球を扱うために使用していました。それらのクラスに対して、次のような処理を行うことが多かったです。
- RectとCircleの中心座標をプロパティX,Yで表していたそれらを変更することがラケットや球の移動として取り扱っていた。
- RectやCircleの大きさ(横幅と高さ)をプロパティWidthとHeightで表していた。それらはラケットの描画や球の衝突判定などに用いていた。
まずはこれらのX,Y,Width,Heightを取り扱う仕組みを作っていきましょう。
IPoint
IPointは2次元の座標を表すインターフェースです。先に挙げたX,Yを取り扱います。
export interface IPoint {
/**
* 基点のX座標
*/
X: number;
/**
* 基点のY座標
*/
Y: number;
}
他の図形はこのIPointをimplementするか、。implementしたクラスを継承するものとします。
ISize
ISizeは2次元の大きさを表すインターフェースです。先に挙げたWidth, Heightを取り扱います。
/**
* 大きさ(サイズ)を持つ図形の基本のインターフェイス
*/
export interface ISize {
/**
* 幅(X方向の大きさ)
*/
get Width(): number;
/**
* 高さ(Y方向の大きさ)
*/
get Height(): number;
}
IPointと異なり、こちらはWidthとHeightのgetterとしました。その理由は、図形によっては必ずしも横幅と高さを実変数として持つ必要がないからです。
例えばCricleでは、円の大きさは横幅と高さで指定するのではなく円の半径で指定していました。そして横幅と高さは半径から算出していました。つまり、図形の横幅や高さを返す(getter)必要はあるかもしれませんが、セットする(setter)必要はありません。そもそも円の場合はWidthとHeightは常に同じです。Widthをセットしたなら同時にHeightを同じ値にしなくてはなりません。
そのためISizeはWidthとHeightのgetterは必要だが、setterは必要ではないというインターフェイスにしました。
基本的な図形クラス
つぎにIPointやISizeをimplmentした図形クラスを定義します。
Point
Pointは2次元の点を表すクラスです。IPointの最も小さなimplementです。点には大きさは存在しないので、ISizeはimplementしません。
import { IPoint } from './IPoint';
/**
* 2次元の点を表すクラス
*/
export class Point implements IPoint {
/**
* X座標
*/
public X: number;
/**
* Y座標
*/
public Y: number;
/**
* コンストラクタ
* @param x X軸座標、default:0
* @param y Y軸座標、default:0
*/
constructor(x: number = 0, y: number = 0) {
this.X = x;
this.Y = y;
}
/**
* srcをコピーする
* @param src
*/
public Copy(src: Point) {
this.X = src.X;
this.Y = src.Y;
}
/**
* クローン(新たなPoint)を作る。
*/
public Clone() {
return new Point(this.X, this.Y);
}
}
Rectangle
Rectangleはすべての辺がX軸と水平あるいは垂直になっている長方形を表すクラスです。大きさを持つので、ISizeをimplementしています。
import { IPoint } from "./IPoint";
import { ISize } from "./ISize";
/**
* 全ての辺がX軸と水平か垂直になっている長方形を表すクラス
*/
export class Rectangle implements IPoint, ISize {
/**
* 中心のX座標
*/
public X: number;
/**
* 中心のY座標
*/
public Y: number;
private _Width: number;
public get Width(): number {
return this._Width;
}
public set Width(value: number) {
if (value >= 0) {
this._Width = value;
}
else {
this._Width = 0;
throw new RangeError("Widthには0以上の値をセットしてください。")
}
}
private _Height: number;
public get Height(): number {
return this._Height;
}
public set Height(value: number) {
if (value >= 0) {
this._Height = value;
}
else {
this._Height = 0;
throw new RangeError("Heightには0以上の値をセットしてください。")
}
}
/**
* コンストラクタ
*
* (x1,y1)と(x2,y2)を対角線上の2点とする四角形
* @param x1
* @param y1
* @param x2
* @param y2
*/
constructor(x1: number, y1: number, x2: number, y2: number) {
this.X = (x1 + x2) / 2;
this.Y = (y1 + y2) / 2;
this._Width = Math.abs(x2 - x1);
this._Height = Math.abs(y2 - y1);
}
/**
* 左辺のX座標
*/
public get Left() { return this.X - this.Width / 2; }
/**
* 右辺のX座標
*/
public get Right() { return this.X + this.Width / 2; }
/**
* 上辺のY座標
*/
public get Top() { return this.Y - this.Height / 2; }
/**
* 下辺のY座標
*/
public get Bottom() { return this.Y + this.Height / 2; }
/**
* srcをコピーする
* @param src
*/
public Copy(src: Rectangle) {
this.X = src.X;
this.Y = src.Y;
this.Width = src.Width;
this.Height = src.Height;
}
/**
* クローン(新たなRectangle)を作る。
* @returns
*/
public Clone() {
return new Rectangle(this.Left, this.Top, this.Right, this.Bottom);
}
}
Rectangleは内部的には、X, Y, Width, Heightの変数で情報を保持し、それを基に、Left, Right, Top, Bottomを計算しています。
Circle
Circleは円を表すクラスです。
import { IPoint } from './IPoint';
import { ISize } from './ISize';
/**
* 円を表すクラス
*/
export class Circle implements IPoint, ISize {
/**
* 中心のX座標
*/
public X: number;
/**
* 中心のY座標
*/
public Y: number;
/**
* 半径
*/
private _Radius: number;
public get Radius(): number {
return this._Radius;
}
public set Radius(value: number) {
if (value >= 0) {
this._Radius = value;
}
else {
this._Radius = 0;
throw new RangeError("Radiusには0より大きな値をセットしてください。")
}
}
/**
* コンストラクタ
*
* 点(x,y)を中心とする半径rの円を作る。
* @param x 中心のX座標
* @param y 中心のY座標
* @param r 中心の半径
*/
constructor(x: number, y: number, r: number) {
this.X = x;
this.Y = y;
if (r >= 0) {
this._Radius = r;
}
else {
this._Radius = 0;
throw new RangeError("半径rは0より大きな値にしてください。")
}
}
get Width(): number {
return this.Radius + this.Radius;
}
get Height(): number {
return this.Radius + this.Radius;
}
/**
* 円の最も元も左のX座標
*/
public get Left() { return this.X - this.Radius; }
/**
* 円の最も右のX座標
*/
public get Right() { return this.X + this.Radius; }
/**
* 円の最も上のY座標
*/
public get Top() { return this.Y - this.Radius; }
/**
* 円の最も下のY座標
*/
public get Bottom() { return this.Y + this.Radius; }
/**
* srcをコピーする
* @param src
*/
public Copy(src: Circle) {
this.X = src.X;
this.Y = src.Y;
this.Radius = src.Radius;
}
/**
* クローン(新たなRectangle)を作る。
* @returns
*/
public Clone() {
return new Circle(this.X, this.Y, this.Radius);
}
}
Circleは、内部的には、X, Y, Radiusの変数で情報を保持し、それを基に、Width, Height, Left, Right, Top, Bottomを計算しています。
コメント