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.cpptoo_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(); } } }