import Color from "/src/engine/data-structure/color.js";
import GameObject from "/src/engine/core/game-object.js";
import { CircleCollider } from "/src/engine/data-structure/collider.js";
import { typeCheck, typeCheckAndClamp } from "/src/engine/utils.js";
import InputManager from "/src/engine/core/input-manager.js";
/**
* 화면에 원을 그리는 객체다.
*
* @extends {GameObject}
*/
class Circle extends GameObject {
/**
* @constructor
* @param {object} options
* @param {string} [options.name]
* @param {number} [options.radius]
* @param {Color} [options.color]
* @param {number} [options.strokeWidth]
* @param {Color} [options.strokeColor]
* @param {boolean} [options.isActive]
* @param {boolean} [options.isVisible]
* @param {Layer} [options.layer]
* @param {boolean} [options.isPhysicsEnable=false]
* @param {object} [options.boundary]
* @param {number} [options.boundary.radius]
* @param {number} [options.boundary.offset]
* @param {object} [options.transform]
* @param {Vector} [options.transform.position=new Vector(0, 0)]
* @param {Vector} [options.transform.scale=new Vector(1, 1)]
* @param {number} [options.transform.rotation=0]
* @param {object} [options.rigidbody]
* @param {number} [options.rigidbody.mass=1]
* @param {number} [options.rigidbody.bounceness=0.5]
* @param {number} [options.rigidbody.staticFriction=0.2]
* @param {number} [options.rigidbody.dynamicFriction=0.1]
* @param {boolean} [options.rigidbody.isStatic=false]
* @param {boolean} [options.rigidbody.isGravity=false]
* @param {boolean} [options.rigidbody.isTrigger=false]
*/
constructor(options = {}) {
super(options);
/**
* 원의 반지름을 의미한다.
* 기본값은 5다.
*
* @type {number}
*/
this.radius = typeCheck(options.radius, "number", 5);
/**
* 윤곽선을 그릴 것인지를 의미한다.
* 윤곽선을 그리기 위해서는 옵션에서 strokeColor나
* strokeWidth를 설정하여야한다.
*
* @type {boolean}
*/
this.isStroke =
options.hasOwnProperty("strokeColor") ||
options.hasOwnProperty("strokeWidth");
if (this.isStroke) {
/**
* 윤곽선의 색상을 의미한다.
* 만약 옵션에서 윤곽선에 대한 정보가 있다면 isStroke는 true로 설정되고
* 윤곽선의 색상이 설정된다.
*/
this.strokeColor = typeCheck(
options.strokeColor,
Color,
new Color(
Math.random() * 255,
Math.random() * 255,
Math.random() * 255,
1
)
);
}
/**
* 윤곽선의 두께를 의미한다.
* 기본값은 1이다.
* 값의 범위는 1 ~ 15다.
*
* @type {number}
*/
this.setStrokeWidth(options.strokeWidth);
/**
* Collision 타입을 원 형태로 설정한다.
*/
if (typeof options.boundary !== "object") {
options.boundary = {};
}
options.boundary.radius = typeCheck(
options.boundary.radius,
"number",
this.radius
);
this.collider = new CircleCollider(options.boundary);
}
/**
* 원의 외형을 반환한다.
* 원의 외형의 크기는 반지름으로 나타내므로, 외형의 반지름이 반환된다.
*
* @returns {number}
*/
getBoundary() {
return this.collider.getBoundary();
}
/**
* 원의 화면상 반지름의 길이를 반환한다.
* 사실 scale이 Vector라서 정확히는 잘못된 함수다.
* 물리엔진에서 scale값을 고려하고 있지만 원에 대해서는 그렇지 않다.
* 이 함수에는 worldScale의 x와 y값을 더한 후 2로 나눈 값을 반지름에 곱하고 있다.
* 가급적이면 원의 scale을 변경하지 않아야 하고,
* 변경하더라도 scale의 x와 y값을 같은 값으로 설정하여야 정상적으로 작동한다.
*
* @returns {number}
*/
getWorldBoundary() {
const worldScale = this.getWorldScale();
return this.getBoundary() * ((worldScale.x + worldScale.y) / 2);
}
/**
* 화면에 원과 윤곽선을 그린다.
*/
draw() {
this.context2d.beginPath();
this.context2d.arc(0, 0, this.radius, 0, 2 * Math.PI);
this.context2d.fillStyle = `rgb(
${this.color.r},
${this.color.g},
${this.color.b}
)`;
this.context2d.fill();
if (this.isStroke) {
this.context2d.lineWidth = this.strokeWidth;
this.context2d.beginPath();
this.context2d.arc(
0,
0,
this.radius - this.strokeWidth / 2,
0,
2 * Math.PI
);
this.context2d.strokeStyle = `rgb(
${this.strokeColor.r},
${this.strokeColor.g},
${this.strokeColor.b}
)`;
this.context2d.stroke();
}
}
/**
* 윤곽선의 두께를 설정한다.
*
* @param {number} width - 윤곽선의 두께
*/
setStrokeWidth(width) {
this.strokeWidth = typeCheckAndClamp(width, "number", 1, 1, 15);
}
/**
* 이 객체위에 마우스가 올라가 있는지를 반환한다.
* 원의 경우 반지름을 이용한 계산을 해야한다.
*
* @returns {boolean}
*/
isMouseOver() {
const position = this.getWorldPosition();
const mousePos = InputManager.getMousePos();
const distance = position.minus(mousePos);
return distance.squareLength() < this.radius * this.radius;
}
}
export default Circle;