Scene Anatomy-English

日6 月Jul 年2021

Scene Structure in C ++ with Butano

Travels of Oortaのコードの構造と仕組みのほとんどは、こちらで読むことができるJonoShieldsによって作成されたコードに基づいています。レベルファイルの各部分を分解し、各セグメントが何をしているのかを説明します。始めましょう!

Most of the Travels of Oorta code structure and mechanics is based on code written by JonoShields, which you can read here (https://jonoshields.com/). Break down each part of the level file and explain what each segment is doing. let's start!

Includes


//-------------------------------------
// オルタの旅 - too_scene_YOURSCENE.cpp
//-------------------------------------

//Scene Header Include, Replace YOURSCENE with your scene name.
#include "too_scene_YOURSCENE.h" //Cooresponding Header for YOURSCENE

//Butano Includes
#include "bn_core.h" //Core Includes
#include "bn_math.h" //Basic Math Functions
#include "bn_log.h" //Logging output to the emulator console
#include "bn_keypad.h" //Checking for Input
#include "bn_string.h" //For dialog and tooltips
#include "bn_fixed_point.h" //For defining coordinates of objects.
#include "bn_sprite_ptr.h" //Sprite Pointers
#include "bn_camera_ptr.h" //Camera Pointers
#include "bn_regular_bg_ptr.h" //Regular Background Pointers
#include "bn_affine_bg_ptr.h" //Affine Background Pointers
#include "bn_affine_bg_map_ptr.h" //Affine Background Tilemap Pointers
#include "bn_optional.h" //Load Optional, for Backgrounds and Sprites.
#include "bn_span.h" //Spans, such as cell data,
#include "bn_affine_bg_map_cell.h" //Affine Background Map Cells
#include "bn_bg_palette_ptr.h" //Background Palette Pointers
#include "bn_bg_palette_actions.h" //Background Palette Actions

//Travels of Oorta Includes
#include "too_level.h" //Processes the Background into a Tilemap with collision data
#include "too_player.h" //Player Mechanics, Input, and Animation
#include "too_scene.h" //Scene Manager for loading and instancing scenes
#include "too_npc.h" //Generic NPC Behaviors and Properties
#include "too_tooltip.h" //Tooltips(Instructional text when near interactive objects)
#include "too_npc_type.h" //There are different types of NPCs
#include "too_enemy.h" //Generic Enemy Behavior and Properties
#include "too_enemy_type.h" //There are different types of Enemies
#include "too_data.h" //Different Flags for game progress(eg. Cutscenes played, Skills learned, Items aquired, Bosses Defeated, etc)
#include "too_story_save.h" //Save the Game and Story progress here.

//Map Cell
#include  //Individual Affine map cells

//Debug
#include "bn_sprite_items_debug.h" //Debugging Icon/Sprite, misc.

//Asset Item Includes (Backgrounds, Tilemaps, Sprites, Palettes, etc)
#include "bn_regular_bg_items_SCENEBACKGROUND.h" //Background Compiled from "/graphics/SCENEBACKGROUND.bmp" and json file
#include "bn_affine_bg_items_SCENETILEMAP.h" //Tilemap Compiled from "/graphics/SCENETILEMAP.bmp" and json file

//Music Items and Actions
#include "bn_music_items.h" //All *.it, *.s3m, *.xm, *.mod music compiled into Soundbank.h
#include "bn_music_actions.h" //Play, Pause, speed, pitch, volume controls and actions

//Text and Font Includes
#include "bn_sprite_text_generator.h" //Object for rendering text with a font. Used for dialog, tooltips, and HUD elements
#include "variable_8x8_sprite_font.h" //Single Vertical Strip of characters in a font 8x8 frames from bmp; Pixel Font



So this is you basic list of includes for a level. They will not necessarily utilize all the Includes, as some areas may lack enemies, NPCs, or even a player, such as a cutscene. Mostly we cover the basics:

  • Scene Background(Static Graphic)
  • Scene Tilemap (The processed tilemap with collision flags etc.)
  • Scene Music(BGM)
  • Scene Audio(SFX)
  • NPCs
  • Enemies
  • Portals/doors

    The List of Defined Scenes, NPCs, and Enemies.

    In addition, there are 3 other files you'll usually be editting:

    too_scene.h

    
    #ifndef BF_SCENE_H
    #define BF_SCENE_H
    namespace too
    {
      enum class Scene
      {
          TITLESCREEN, //TitleScreen Scene
          MAINMENU, //Main Menu
          CUTSCENE1, //Intro Cutscene
          OPTIONS, //Options Menu
          LIMBO1, //First Area where the player spawns
          LIMBO1_LIMBO2, //Going from Limbo1 to Limbo2
          LIMBO2_LIMBO1, //Going from Limbo2 to Limbo1
          LIMBO2_LIMBO3, //Going from Limbo2 to Limbo3
          LIMBO2, //Just Limbo2
          LIMBO3, //Just Limbo3
          LIMBO3_LIMBO2,
          LIMBO3_SUMMER1,
          SUMMER1_LIMBO3, //Same as above, FROM_TO
          SUMMER1_SUMMER2,
          SUMMER2_SUMMER1,
          LOADING // Loading Scene
      };
    }
    #endif
    



    Add an uppercase title for your Scene, such as DUNGEONENTRANCE or DUNGEON_COURTYARD. You can include standalone rooms, which may not have a portal or entrance/exit. Useful to just plug in to the main starting scene in too_scene_maingame if you need to go to different levels.

    too_npc_type.h

    
    //-------------------
    //From too_npc_type.h
    //-------------------
    #ifndef TOO_NPC_TYPE_H
    #define TOO_NPC_TYPE_H
    namespace too
    {
      enum class NPC_TYPE
      {
          OLD_MAN, //It's Dangerous, Take this
          CATS, //All your base are belong to us
          LOSHA, //Food NPC from Flyff
          FROG, //Another type of NPC
          MOTH_MAN //Flying NPC
      };
    }
    #endif
    



    too_npc.h

    Same as above, but instead of Scenes as Entrances, we are listing the NPCs types we have available to the level. You can customize the way the NPC type works in too_npc.cpp, like this excerpt:

    
    //----------------
    //From too_npc.h
    //----------------
    //NPC Type FROG
          if(_type == NPC_TYPE::FROG){
              _sprite = bn::sprite_items::frog_sprite.create_sprite(_pos.x(), _pos.y()); //NPC Sprite and position
              _action = bn::create_sprite_animate_action_forever( //NPC Animation Action
                              _sprite.value(), 40, bn::sprite_items::frog_sprite.tiles_item(), 0,1);
              _lines = bn::span(_frog_lines); //NPC Dialog text
          }
          _sprite.value().set_camera(_camera); //Camera reference
          _sprite.value().set_bg_priority(1); //Render Priority
          _sprite.value().set_z_order(2); //Sprite Depth Sorting
      }
    //Extra NPC Types Below, such as special NPCs that trigger cutscenes or battles.
    



    Add your NPC behavior and interaction code in the corresponding *.cpp files as well if you need to.

    too_enemy_type.h

    Here's where you define an enemy type on the list of types of enemies. Add a title for your enemy into this header file's enumerated list:

    
    //----------------
    //From too_enemy_type.h
    //----------------
    #ifndef BF_ENEMY_TYPE_H
    #define BF_ENEMY_TYPE_H
    namespace too
    {
      enum class ENEMY_TYPE
      {
          BAT,
          SLIME,
          AIBAT, //Level 1 masquerpet from Flyff
          MUSHPANG, //Another Flyff monster
          BURUGDEN, //And another...etc
          PUKEPUKE,
          LAWOLF,
          FEFERN,
          BANG,
          MOTH //Flying Enemy from Flyff
      };
    }
    #endif
    



    too_enemy.h

    In this file you can customize your enemies' behavior and logic/etc.

    
    //-------------------
    // From too_enemy.cpp
    //-------------------
          if(_type == ENEMY_TYPE::BAT)
          {
              _sprite = bn::sprite_items::bat_sprite.create_sprite(_pos.x(), _pos.y()); //Set Enemy "BAT"'s sprite.
              _sprite.value().set_camera(_camera); //Assign Camera
              _sprite.value().set_bg_priority(1); //Set Background Priority
              _action = bn::create_sprite_animate_action_forever( //Set Animation Action
                              _sprite.value(), 4, bn::sprite_items::bat_sprite.tiles_item(), 0,1,0,1);
          } else if (_type == ENEMY_TYPE::SLIME){ //Same as above, but for "SLIME"
              _sprite = bn::sprite_items::slime_sprite.create_sprite(_pos.x(), _pos.y());
              _sprite.value().set_camera(_camera);
              _sprite.value().set_bg_priority(1);
              _action = bn::create_sprite_animate_action_forever(
                               _sprite.value(), 20, bn::sprite_items::slime_sprite.tiles_item(), 0,1,0,1);
          }
    



    This allows you to customize the properties, such as sprite, animation, and other traits of your enemies. There are also shared generic traits of the enemies, such as getting hit, death, and collision behavior.

    Structure of the Scene.cpp file.

    So back to this too_scene_YOURSCENE.h and too_scene_YOURSCENE.cpp files...
    You will need to create a file named too_scene_YOURSCENE.h as well as too_scene_YOURSCENE.cpp

    too_scene_YOURSCENE.h Template

    
    //----------------------
    //too_scene_YOURSCENE.h Template
    //----------------------
    #ifndef TOO_SCENE_YOURSCENE_H
    #define TOO_SCENE_YOURSCENE_H
    #include "too_scene.h"
    #include "too_player.h"
    #include "bn_fixed_point.h"
    namespace too
    {
      class YourScene
      {
          public:
              Scene execute(Player& player, bn::fixed_point spawn);
      };
    }
    #endif
    



    Putting this .h file allows you to reference it in too_scene_maingame.c, So you can include it in "too_scene_maingame.cpp":

    
    //---------------------------------------------------------------------------------
    //Include the file in your level's *.cpp file as well as "too_scene_maingame.cpp".
    //---------------------------------------------------------------------------------
    //too_scene_maingame.cpp:
    #include "too_scene_YOURSCENE.h"
    //too_scene_YOURSCENE.cpp
    #include "too_scene_YOURSCENE.h"
    



    You can include the .h file in both other scenes that use the constructors to initialize the level, as well as include it in your level's .cpp file.

    
    //----------------------------------------------
    //too_scene_maingame.cpp -> MainGame::MainGame()
    //----------------------------------------------
    MainGame::MainGame(bn::sprite_text_generator& text_generator)
      {
          too::Scene scene = too::Scene::YOURSCENE; //Here we define the starting Scene
          too::Loading loading = too::Loading(); //Preload the loading screen
          bn::sprite_ptr cat_sprite = bn::sprite_items::cat_sprite.create_sprite(0,0); //Global Player Sprite
          too::Player player = too::Player(cat_sprite, text_generator); //Global Player instance.
          while(true)
          {
             //Starting Scene Room
              if(scene == too::Scene::YOURSCENE) //IF
              {
                  too::YourScene yourscene = too::YourScene(); // Make a new scene variable called yourscene, initialized with YourScene() from your level's .h file.
                  scene = yourscene.execute(player, bn::fixed_point(112, 208)); //Set "Scene", the current scene, to the initialized constructor of your scene, passing spawn coordinates as a parameter.
              }
              //Entering "NewScene" from "YourScene"
              else if(scene == too::Scene::YOURSCENE_NEWSCENE) //Same as before, but the spawn point is in the next area, as if coming from the previous scene.
              {
                  too::YourScene to_newscene_from_yourscene = too::NewScene();
                  scene = to_newscene_from_yourscene.execute(player, bn::fixed_point(944, 736));
              }
              cat_sprite.set_visible(false);
              loading.execute();
              bn::core::update();
         };
      };
    



    Edit the "if()" and "else if()" statements and sections to match the case and spelling of your .h file's title, (eg. YOURSCENE and YourScene::YourScene()) Every Level has a constructor you can name, but most levels require a spawn point. Other scenes pass text_generator pointers or other data to initialize and process in the level, such as a cinematic cutscene or menu. Make sure you are using correct case for conventions, it's easier to be consistent like this.

    You should make a list of all your rooms and portals, and match these spawn points to entrances and exits. Every Entrance is an Exit, and every Exit is an Enterance, keep in mind.

    Structure of a Scene (too_scene_yourscene.cpp)

    Here I have commented the different parts of your scene file, you can use the comments as a guide for where to put different snippets of code for different elements of the level. I named my scene "SUMMER2", so conventions should have the constructor in the .h file be something like "Summer1::Summer1()" and variables for it should be summer1 etc. Be sure the file has all the includes you need from the very start of this page.

    
    //--------------------------
    //From too_scene_summer2.cpp
    //--------------------------
    namespace too
    {
      //Scene Summer1
      Scene Summer2::execute(Player& player, bn::fixed_point spawn_location)
      {
          //Camera
          bn::camera_ptr camera = bn::camera_ptr::create(spawn_location.x(), spawn_location.y());
          //Text Generator
          bn::sprite_text_generator text_generator(variable_8x8_sprite_font);
          //Play BGM
          bn::music_items::mountains.play();
          // Tilemaps and Backgrounds
          bn::regular_bg_ptr map_bg = bn::regular_bg_items::background.create_bg(0, 0);
          bn::affine_bg_ptr map = bn::affine_bg_items::summer2.create_bg(512, 512);
          //Background Priorities and Scale
          map_bg.set_priority(3);
          map.set_priority(2);
          too::Level level = too::Level(map);
          // Camera
          map.set_camera(camera);
          //Enemies
          bn::vector enemies = {};
              //enemies.push_back(Enemy(240, 912, camera, map, ENEMY_TYPE::SLIME, 2));
              //enemies.push_back(Enemy(416, 912, camera, map, ENEMY_TYPE::SLIME, 2));
              //enemies.push_back(Enemy(720, 432, camera, map, ENEMY_TYPE::BAT, 1));
              //enemies.push_back(Enemy(480, 192, camera, map, ENEMY_TYPE::BAT, 1));
          // Initialize Player
          player.spawn(spawn_location, camera, map, enemies);
          //Initialize Portals/Savepoints
          StorySave summer2_summer1 = StorySave(bn::fixed_point(128, 928), STORY_TYPE::BEGINNING, camera, text_generator);
          //Game Loop
          while(true)
          {
              //Enemies
              for(Enemy& enemy : enemies){
                  if(bn::abs(enemy.pos().x() - camera.x()) < 240 && bn::abs(enemy.pos().y() - camera.y()) < 160){
                      enemy.update();
                  } else {
                      enemy.set_visible(false);
                  }
              }
              //Spawn Points and Portals
              summer2_summer1.update();
              if(bn::keypad::up_pressed())
              {
                  if(player.pos().x() < 128+16 && player.pos().x() > 128-16){
                      if(player.pos().y() < 928+16 && player.pos().y() > 928-16){
                          return Scene::SUMMER2_SUMMER1;
                      }
                  }
              }
              //Player Updatess
              player.update_position(map, level, text_generator);
              player.apply_animation_state();
              //Update Frame
              bn::core::update();
          }
      }
    }