Files
DrawingSongHLE/Assets/Code/MainController.cs
2020-12-05 19:34:02 -07:00

249 lines
7.9 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
public class MainController : MonoBehaviour
{
bool isMenu;
bool loaded;
Language cachedLanguage;
byte[] cachedBanner;
int menuTick;
float frameTimer;
float timePerFrame;
public ResourceManager resourceManager;
public AudioController audioController;
public AnimationDecoder animationDecoder;
public Framebuffer framebuffer;
public bool isPlaying { get; private set; }
public Language currentLanguage { get; private set; }
public int mainTick { get; private set; }
// Start is called before the first frame update
void Start()
{
timePerFrame = 1 / 60f;
ResetAll();
}
// Update is called once per frame
void Update()
{
CheckAndLoadResources();
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
if (currentLanguage == 0)
currentLanguage = (Language)((int)Language.Max - 1);
else
currentLanguage -= 1;
}
if (Input.GetKeyDown(KeyCode.RightArrow))
{
if (currentLanguage == Language.Max - 1)
currentLanguage = 0;
else
currentLanguage += 1;
}
if (Input.GetKeyDown(KeyCode.Return))
{
if (isMenu)
{
isMenu = false;
isPlaying = true;
}
}
if (Input.GetKeyDown(KeyCode.Escape))
{
if (!isMenu) isPlaying = !isPlaying;
}
if (Input.GetKeyDown(KeyCode.F5))
{
ResetAll();
}
frameTimer += Time.deltaTime;
//Debug.Log($"Time: dt = {Time.deltaTime}, ideal time per frame = {timePerFrame}");
bool shouldFlip = false;
while (frameTimer >= timePerFrame)
{
frameTimer -= timePerFrame;
Profiler.BeginSample("Main frame update");
animationDecoder.decodingActive = true;
FrameUpdate();
Profiler.EndSample();
shouldFlip = true;
}
if (shouldFlip) framebuffer.FrameUpdate();
}
private void FrameUpdate()
{
if (!loaded) return;
if (animationDecoder.framesDecoded == 0 || isPlaying && !isMenu)
{
if (animationDecoder.decodingActive)
{
Profiler.BeginSample("Animation decoder update");
animationDecoder.AnimUpdate();
Profiler.EndSample();
}
}
if (animationDecoder.framesDecoded == 0) return;
// Render to framebuffer
// First, create a buffer for the frame surrounding the animation area
const int SIDEBUF_WIDTH = 6;
const int SIDEBUF_HEIGHT = 6;
byte[] sideBuf = new byte[SIDEBUF_WIDTH * SIDEBUF_HEIGHT];
for (int y = 0; y < SIDEBUF_HEIGHT; ++y)
for (int x = 0; x < SIDEBUF_WIDTH; ++x)
{
sideBuf[(y + 2) % SIDEBUF_HEIGHT * SIDEBUF_WIDTH + ((x + 2) % SIDEBUF_WIDTH)] = animationDecoder.indexedPixels[y, x];
}
// Draw left and right frame and main anim area
for (int y = Config.animYPos; y < Config.animYPos + Config.ANIMATION_HEIGHT; ++y)
{
for (int x = 0; x < Config.animXPos; ++x)
{
framebuffer.SetPixel(x, y, Config.palette[sideBuf[y % SIDEBUF_HEIGHT * SIDEBUF_WIDTH + (x % SIDEBUF_WIDTH)]]);
}
for (int x = 0; x < Config.ANIMATION_WIDTH; ++x)
{
framebuffer.SetPixel(Config.animXPos + x, y, Config.palette[animationDecoder.indexedPixels[y - Config.animYPos, x]]);
}
for (int x = Config.animXPos + Config.ANIMATION_WIDTH; x < Config.FRAMEBUFFER_WIDTH; ++x)
{
framebuffer.SetPixel(x, y, Config.palette[sideBuf[y % SIDEBUF_HEIGHT * SIDEBUF_WIDTH + (x % SIDEBUF_WIDTH)]]);
}
}
// Draw top frame
for (int y = 0; y < Config.animYPos; ++y)
for (int x = 0; x < Config.FRAMEBUFFER_WIDTH; ++x)
{
framebuffer.SetPixel(x, y, Config.palette[sideBuf[y % SIDEBUF_HEIGHT * SIDEBUF_WIDTH + (x % SIDEBUF_WIDTH)]]);
}
// Clear bottom with black
for (int y = Config.animYPos + Config.FRAMEBUFFER_HEIGHT; y < Config.FRAMEBUFFER_HEIGHT; ++y)
for (int x = 0; x < Config.FRAMEBUFFER_WIDTH; ++x)
{
framebuffer.SetPixel(x, y, 0x0000);
}
if (cachedLanguage != currentLanguage || cachedBanner == null)
{
cachedLanguage = currentLanguage;
cachedBanner = resourceManager.GetLanguageBanner(currentLanguage);
}
if (isMenu || animationDecoder.framesDecoded < 8)
{
// Draw banner
int bannerYAdjust = Config.langBannerHeightAdjustments[(int)currentLanguage];
byte b = 0;
byte bitsRemaining = 0;
int i = 0;
for (int y = bannerYAdjust; y < Config.langBannerHeight - 2 * bannerYAdjust; ++y)
for (int x = 0; x < Config.ANIMATION_WIDTH; ++x)
{
if (bitsRemaining == 0)
{
b = cachedBanner[i++];
bitsRemaining = 8;
}
framebuffer.SetPixel(x + Config.animXPos, y + Config.langBannerYOffset, (b & 1) == 1 ? Config.langBannerFgColor : Config.langBannerBgColor);
b >>= 1;
--bitsRemaining;
}
}
if (isMenu)
{
if (menuTick % 0x40 < 0x30)
{
// Draw arrows and frame
DrawHorizontalTriangle(8, 60, 20, 18, Config.langBannerFgColor);
DrawHorizontalTriangle(312, 60, -20, 18, Config.langBannerFgColor);
DrawRectFrame(32, 27, 288, 93, Config.langBannerFgColor);
}
++menuTick;
}
}
void DrawHorizontalTriangle(int x, int y, int width, int height, ushort color)
{
if (width != 0 && height != 0)
{
int direction = width < 1 ? -1 : 1;
width *= direction;
int maxDist = 1;
int i = 0;
for (int xOff = 0; xOff < width; ++xOff)
{
for (int currDist = 0; currDist < maxDist; ++currDist)
{
framebuffer.SetPixel(x + xOff * direction, y + currDist, color);
if (currDist != 0)
{
framebuffer.SetPixel(x + xOff * direction, y - currDist, color);
}
}
i += height;
if (i >= width)
{
++maxDist;
i -= width;
}
}
}
}
void DrawRectFrame(int left, int top, int right, int bottom, ushort color)
{
for (int x = left; x <= right; ++x)
{
framebuffer.SetPixel(x, top, color);
framebuffer.SetPixel(x, bottom, color);
}
// Note: original condition was top..bottom, but since we covered it with above,
// we can skip two rows
for (int y = top + 1; y < bottom; ++y)
{
framebuffer.SetPixel(left, y, color);
framebuffer.SetPixel(right, y, color);
}
}
void CheckAndLoadResources()
{
if (!loaded && resourceManager.isReady)
{
audioController.LoadResources();
animationDecoder.LoadResources();
loaded = true;
}
}
void ResetAll()
{
audioController.ResetController();
animationDecoder.ResetController();
loaded = false;
isPlaying = false;
isMenu = true;
menuTick = 0;
cachedBanner = null;
}
}