A lightweight Java/AWT-based 2D game engine. It supports high-performance memory management, dynamic texture atlas packing, an in-game developer console, and a completed Hangul (Korean) input module.
- Initialization
- Changed from relying on default Java constructors to explicit initialization.
- Only
super()is now recommended inside the constructor. - Updated
AssetInitso assets initialized during boot can immediately return proxy objects.
- Performance
- Added
PerformanceRecorderto monitor and dump runtime performance data. - Added
PerformanceReaderin releases to read dump files.
- Added
- Sound
- Rolled back from the off-heap
BeisiqTinySoundimplementation to the originalTinySoundfor stability.
- Rolled back from the off-heap
-
Advanced Graphics Pipeline
- Double-buffering rendering pipeline based on
VolatileImageandBufferStrategy - Dynamic Texture Atlas Generation and Packing based on real-time Alpha Cropping and MaxRects algorithms
- Automatic display scaling calculation (Fractional / Integer Physical Scaling) and view metrics management
- Double-buffering rendering pipeline based on
-
High-Performance Sound Engine (TinySound Based)
- Built-in low-latency audio processing pipeline based on an audio mixer
- Supports file streaming (
StreamMusic,StreamSound) and memory resident (MemMusic,MemSound) playback - Supports BGM streaming, SFX overlapping playback, real-time panning, and volume control
-
Developer Experience & Tooling
- In-game developer console (
~key support, system control, GC memory cleanup, tree-based auto-completion viaAutoCompleteManager) - Built-in
TextModule(TextObject) providing full support for Korean composition input and clipboard pasting in an AWT environment - AES encryption-based data save/load (
IoObject,DynamicIoLoadObject) and async/dynamic asset manager (AssetManager) - Runtime performance profiling and dump viewer tooling (
PerformanceRecorder,PerformanceReader)
- In-game developer console (
- JDK: OpenJDK 21+ (GraalVM 21+ recommended)
- OS: Windows / macOS / Linux (probably)
Configure settings via Core.setConfig, then inherit and use the Base class.
public class MyGame extends Base {
static {
Core.setConfig(new Config.Builder("MyGameFolder") // Creates a project folder at ~/.MyGameFolder
.setWindowWidth(1280) // Sets actual window size (virtual resolution is fixed to Full HD 1920x1080)
.setWindowHeight(720)
.setUseKoreanModule(true) // Enables the Korean input module when true
.setUseIntegerPhysicalScaling(true) // Snaps physical scaling to integer values when true
.setUseEncryption(false) // Applies AES encryption to save files when true
.setEncryptionKey("keyforencryption") // 16-character encryption key
.build());
}
public MyGame() {
super(new Builder()
.setUseConsole(true) // Enables in-game developer console
.setRenderingOption(RenderingOption.DEFAULT) // Rendering option (DEFAULT / LEGACY / EXPERIMENTAL)
.setCloseWindowWithKillVM(true) // Terminates process completely upon window close
);
}
@Override
public void init(BaseInit init) {
// Allocate and initialize object pools
assetManager.mallocTexturePool(1000);
assetManager.mallocLazyLoadPool(200);
// Register initial scene and bind atlas/audio
init.setInitScene(new Scene.Builder(sceneInit -> {
sceneInit.createAtlas("DEFAULT_ATLAS", 2, binder -> {
// binder.registerSprite(IoUtils.getGameResourceStream("sample.png"), "sample");
});
}).setName("MAIN_SCENE").build());
}
@Override
public void update(double dt) {
// Update game logic
}
@Override
public void render(Graphics2D g) {
// Rendering code
g.setColor(Color.WHITE);
g.drawString("Hello, Beisiq Engine!", 50, 50);
}
public static void main(String[] args) {
new MyGame().launch();
}
}The Base class is the core context responsible for engine execution, thread and rendering loop management, scene lifecycle, and safe shutdown handling.
A sequential initialization phase executed on a background thread (Async-Loader) after engine instantiation.
- Bootstrapping (Main Thread)
Coreconfiguration validation andJFrame/Canvaswindow creation (windowSetup)BufferStrategy(2)andViewMetricssetup- Starts logic and render threads by calling
launch()
- Async Initialization (
Async-Loader Thread)sysLoadStack: Initializes system modules (console, mouse interface, asset directories, etc.)assetInit: Loads boot-stage textures, sound, and music proxies, and binds actual targetsio: Loads save and configuration data (io.load.load())sceneInit: Invokes the designated initialScene.init()and completes (initLoadEnd = true)
Logic and rendering run on independent threads. (Target: 60 FPS)
- Calculates Delta Time (
dt) and executesupdate(dt) - Updates profiler (
PerformanceRecorder) and detects scene transition requests (isChangeScene) - Controls precision sleep timing using
Thread.sleep()andThread.yield()
- Suspends rendering during window resizing for
RESIZE_SETTLE_NANOSto prevent flickering - Displays loading screen (
renderLoadingScreen) and error overlay (ErrorBoxManager) during loading states - Updates screen based on configured
RenderingOption:DEFAULT: Standard double-buffering (BufferStrategy) renderingLEGACY: VRAM memory buffer (VolatileImage) renderingEXPERIMENTAL: Batched rendering after draw call buffer caching (Extremely low performance)
Memory release and transition workflow executed upon calling changeScene(newScene).
changeScene(newScene)- └─► Set
pendingSceneandisChangeScene = true - └─► Invoke previous Scene's
dispose()(Reflection) - └─► Collect unused texture garbage queue (
assetManager.clearGarbage()) and perform explicit GC (System.gc()) - └─► Asynchronously execute new
Scene.init()and complete transition (isChangeScene = false)
- └─► Set
Safely releases resources and terminates the process upon calling exit().
- Save Data: Immediately synchronizes save data by invoking
io.save.save() - Hook Execution: Batch processes shutdown tasks linked to
operatorManager.exitOperatorPack.launch() - Thread Join: Awaits safe termination of
logicThreadandrenderThreadwithin timeout - Dispose: Fully releases and disposes of
BufferStrategyandJFrameresources
Unstable Sound Engine.oggextension unavailable- Non-streaming
Musicusage unavailable Mono channel audio gets corrupted when loaded into memory without streaming
- Unstable API
- Incomplete encapsulation
- Unstable API design
This project is a hobby project developed by a single developer. As such, stabilization may take time, and documentation progress will be slower.
Acknowledging these limitations, a PDF containing the full source code will be attached to major version releases. Feel free to utilize it for training AI models or other reference purposes.
Java/AWT 기반의 경량 2D 게임 엔진입니다. 고성능 메모리 관리, 동적 텍스처 아틀라스 패킹, 인게임 개발자 콘솔 및 완성형 한글 입력 모듈을 지원합니다.
- 초기화
- 자바 기본 생성자의 의존하던 방식에서 명시적 초기화로 변경
- 이제는 생성자에는
super()말고는 아무것도 권장되지 않음 AssetInit에서 부팅시 초기화하는 에셋을 즉시 프록시로 객체를 받을수 있도록 변경
- 성능
- 성능을 모니터링하고 덤프하는
PerformanceRecorder추가 - 릴리즈의
PerformanceReader로 읽기가능
- 성능을 모니터링하고 덤프하는
- 사운드
Off-Heap을 사용하던BeisiqTinySound기반에서 안정성을 위해 오리지널TinySound로 롤백
-
Advanced Graphics Pipeline
VolatileImage및BufferStrategy기반의 이중 버퍼링 렌더링 파이프라인 지원- 실시간 알파 크롭(Alpha Cropping) 및 맥스렉츠(MaxRects) 알고리즘 기반 동적 텍스처 아틀라스(Atlas) 생성 및 패킹
- 디스플레이 스케일링(Fractional / Integer Physical Scaling) 자동 계산 및 뷰 메트릭 관리
-
High-Performance Sound Engine (TinySound Based)
- 오디오 믹서 기반의 저지연 오디오 프로세싱 파이프라인 내장
- 파일 스트리밍(
StreamMusic,StreamSound) 및 메모리 상주(MemMusic,MemSound) 방식 지원 - BGM 스트리밍, SFX 오버랩 재생, 실시간 패닝(Pan) 및 볼륨 제어 지원
-
Developer Experience & Tooling
- 인게임 개발자 콘솔 (
~키 지원, 시스템 제어, GC 메모리 정리, 트리 기반 규칙 자동 완성AutoCompleteManager제공) - AWT 환경의 한글 조합 및 클립보드 붙여넣기를 완벽 지원하는
TextModule(TextObject) 내장 - AES 암호화 기반의 데이터 저장/로드(
IoObject,DynamicIoLoadObject) 및 비동기/동적 에셋 관리자(AssetManager) - 런타임 성능 프로파일링 및 덤프 뷰어 도구(
PerformanceRecorder,PerformanceReader) 지원
- 인게임 개발자 콘솔 (
- JDK: OpenJDK 21+ (GraalVM 21+ 권장)
- OS: Windows / macOS / Linux(아마도)
Core.setConfig를 수행한 후 Base 클래스를 상속받아 사용합니다.
public class MyGame extends Base {
static {
Core.setConfig(new Config.Builder("MyGameFolder") // ~/.MyGameFolder 경로에 프로젝트 폴더가 생성됩니다.
.setWindowWidth(1280) // 가상 해상도는 Full HD(1920x1080) 기준이며 실제 창 크기를 설정합니다.
.setWindowHeight(720)
.setUseKoreanModule(true) // 활성화 시 한글 입력 모듈이 활성화됩니다.
.setUseIntegerPhysicalScaling(true) // 정수 단위 물리 배율 스냅 여부를 설정합니다.
.setUseEncryption(false) // 활성화 시 저장 파일에 AES 암호화가 적용됩니다.
.setEncryptionKey("keyforencryption") // 암호화 시 사용할 16자리 키를 설정합니다.
.build());
}
public MyGame() {
super(new Builder()
.setUseConsole(true) // 인게임 콘솔 활성화
.setRenderingOption(RenderingOption.DEFAULT) // 렌더링 옵션 (DEFAULT / LEGACY / EXPERIMENTAL)
.setCloseWindowWithKillVM(true) // 창 종료 시 프로세스 완전 종료 여부
);
}
@Override
public void init(BaseInit init) {
// 오브젝트 풀 할당 및 초기화
assetManager.mallocTexturePool(1000);
assetManager.mallocLazyLoadPool(200);
// 초기 씬 등록 및 아틀라스/오디오 바인딩
init.setInitScene(new Scene.Builder(sceneInit -> {
sceneInit.createAtlas("DEFAULT_ATLAS", 2, binder -> {
// binder.registerSprite(IoUtils.getGameResourceStream("sample.png"), "sample");
});
}).setName("MAIN_SCENE").build());
}
@Override
public void update(double dt) {
// 게임 로직 업데이트
}
@Override
public void render(Graphics2D g) {
// 렌더링 코드
g.setColor(Color.WHITE);
g.drawString("Hello, Beisiq Engine!", 50, 50);
}
public static void main(String[] args) {
new MyGame().launch();
}
}Base 클래스는 엔진의 실행, 스레드 및 렌더링 루프 관리, 씬 라이프사이클, 안전한 종료 처리를 담당하는 핵심 콘텍스트입니다.
엔진 생성 후 백그라운드 스레드(Async-Loader)에서 진행되는 순차적 초기화 단계입니다.
- Bootstrapping (Main Thread)
Core설정 검증 및JFrame/Canvas창 생성 (windowSetup)BufferStrategy(2)및ViewMetrics구성launch()호출을 통한 로직/렌더 스레드 구동
- Async Initialization (
Async-Loader Thread)sysLoadStack: 콘솔, 마우스 인터페이스, 에셋 폴더 등 시스템 모듈 구성assetInit: 부트 단계 필수 텍스처, 사운드, 음악 프록시 실제 로딩 및 타깃 바인딩io: 세이브 및 설정 데이터 로드 (io.load.load())sceneInit: 최초 지정된Scene.init()호출 후 완료 처리 (initLoadEnd = true)
로직과 렌더링이 상호 독립된 스레드에서 구동됩니다. (Target: 60 FPS)
- Delta Time(
dt)을 계산하여update(dt)실행 - 프로파일러(
PerformanceRecorder) 갱신 및 씬 변경 상태(isChangeScene) 감지 Thread.sleep()및Thread.yield()를 활용한 Precision Sleep 정밀 타이밍 제어
- 윈도우 크기 변경 시
RESIZE_SETTLE_NANOS동안 렌더 연산 대기 (화면 깜빡임 방지) - 로딩 중일 경우 로딩 화면(
renderLoadingScreen) 및ErrorBoxManager에러 오버레이 렌더링 - 설정된
RenderingOption모드에 따른 화면 갱신:DEFAULT: Double Buffering (BufferStrategy) 기반 표준 렌더링LEGACY: VRAM 메모리 버퍼 (VolatileImage) 기반 렌더링EXPERIMENTAL: Draw Call 버퍼 캐싱 연산 후 일괄 렌더링 (성능이 극히 낮음)
changeScene(newScene) 호출 시 수행되는 메모리 해제 및 전환 흐름입니다.
changeScene(newScene)- └─►
pendingScene설정 및isChangeScene = true - └─► 이전 Scene의
dispose()호출 (Reflection) - └─► 미사용 텍스처 가비지 큐 수거(
assetManager.clearGarbage()) 및 명시적 GC(System.gc()) 수행 - └─► 신규
Scene.init()비동기 실행 및 전환 완료 (isChangeScene = false)
- └─►
exit() 호출 시 리소스를 안전하게 정리하고 프로세스를 종료합니다.
- Save Data:
io.save.save()호출로 세이브 데이터 즉시 동기화 - Hook Execution:
operatorManager.exitOperatorPack.launch()연동 종료 작업 일괄 처리 - Thread Join:
logicThread,renderThread타임아웃 대기 후 안전한 루프 종료 - Dispose:
BufferStrategy및JFrame리소스 완전 해제 및 반납
Unstable Sound Engineogg확장자 사용 불능- 스트림 옵션이 아닌
Music사용 불능 모노 채널 사운드가 스트림이 아닌 메모리에 올라갈 때 깨짐 현상 발생
- Unstable API
- 완벽하지 않은 캡슐화
- 일부 불안정한 API 구성
이 프로젝트는 개발자 1명이 취미로 하는 프로젝트입니다. 이로써 빨리 안정되지 않을 수 있으며 문서화는 더 더딜 것입니다.
이 한계도 저는 알기 때문에 크게 바뀌는 엔진 버전의 릴리스에는 모든 코드의 전문이 담긴 PDF를 첨부합니다. AI에게 학습시키는 등의 방법으로 활용하시길 바랍니다.