Repository navigation
Scenes
Scenes are frames in a visual novel game. They typically contain a background image and character dialogue. In RenJava there are several types of scenes such as;
- AnimationScene - Contains a video instead of an image which plays in the background.
- AutoPlayScene - Automatically displays and runs the next scene after a set duration.
- ImageScene - Your standard visual novel scene. Background image and a textbox, both of which are optional.
- InteractableScene - A little more complex of a scene, does not automatically end.
- ChoiceScene - Contains choices for the player to select.
- InputScene. Contains an input field.
In order to use and display the scenes you will have to create a story first. The init() function is then used to add scenes the story. I like to view stories as chapter and character events. Stories handle and map scenes to display and run them in the proper order. You will also have to map the story to the framework.
public class MyStory extends Story {
public MyStory(String id) {
super(id);
}
@Override
public void init() {
// Inside this function you need to add your scenes to the story.
this.addScene(RenScene scene);
}
}To register and map the story to the framework, simply create an object instance of the class.
public class MyRenJavaClass extends RenJava {
...
@Override
public void createStory() {
new MyStory("my-story");
}
}There are also generic scene functions which can be used to control how the scene plays.
/**
* Creates an event handler to execute when a scene starts.
* @param sceneInterface Code to execute.
* @return The modified RenScene.
*/
public RenScene onStart(SceneStartInterface sceneInterface) {
this.startInterface = sceneInterface;
return this;
}
/**
* Create an event handler to execute when a scene ends.
* @param endInterface Code to execute.
* @return The modified RenScene.
*/
public RenScene onEnd(SceneEndInterface endInterface) {
this.endInterface = endInterface;
return this;
}
/**
* Create an event handler to execute when a menu is built not rendered.
* @param buildInterface Code to execute.
* @return The modified RenScene.
*/
public RenScene onBuild(SceneBuildInterface buildInterface) {
this.buildInterface = buildInterface;
return this;
}Here is some examples of how you can use these functions.
ImageScene scene = new ImageScene("1", nar, "After some struggle and rolling out of the bed. I make my way into the town.");
scene.onEnd(event -> {
// Prevents the engine from advancing to the next scene.
event.setAutoPlayNextScene(false);
// Start next story.
RenJava.getInstance().getPlayer().startStory("next-story");
});
scene.onStart(event -> {
// Start music to set the mood for the scene. Sometimes it is best to set music for when the story starts.
RenJava.getInstance().getTracks().play("happymusic.mp3", true);
});There are a lot of ways to control and manage how stories work and function. You can control specific scenes in a story and event multiple. Stories can also have global controls, such as, setting music for the entire story.
There are global story events that handle the start and end of those stories.
Story story = RenJava.getInstance().getStory("story");
story.onStart(event -> {
// Play music when the story starts
RenJava.getTracks().playMusic("music.mp3", true);
})
story.onEnd(event -> {
// When the story ends start the next one.
Story nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
nextStory.start();
})All stories must be mapped to the framework in 'createStory()' function. It is not required but highly recommended to do it in chronological order.
public class MyGame extends RenJava {
...
@Override
public void createStory() {
new MyFirstStory("story-id");
new MySecondStory("second-story");
new MyThirdStory("third-story");
}
}
public class MyFirstStory extends Story {
public MyFirstStory(String id) {
super(id);
}
@Override
public void init() {
// Add scenes here.
}
}You can also map stories within the story constructor. I do not recommend doing it this way but as I find it personally harder to keep track.
public class MyFirstStory extends Story {
public MyFirstStory(String id) {
super(id);
new MySecondStory("second-story");
}
@Override
public void init() {
// Add scenes here.
}
}
public class MySecondStory extends Story {
public MySecondStory(String id) {
super(id);
new MyThirdStory("third-story");
}
@Override
public void init() {
// Add scenes here.
}
}The main aspect of VN games are the different routes a player can take, the way a story can shape itself with the players decisions. The API was shaped around this idea and mechanic. Players can make choices by using the ChoiceScene.
ChoiceScene choiceScene = new ChoiceScene("4");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));
// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
// This is an action listener. The code here is called when they click a choice.
Choice choice = event.getChoice(); // Gets the choice button they clocked.
if (choice.getId().equalsIgnoreCase("help")) {
// The player selected the help choice
} else if (choice.getID().equalsIgnoreCase("run")) {
// The player decided to run away
}
})Events are the main function to shape your story. Different scenes may have specific events built in them while some may not. All scenes will have start and end events. It is imperative you pick the proper scene to shape your story.
There are a few ways you can map your story with different routes. Depending on the impact of the decision you may have to create a separate story entirely. The below is an example of dealing with different choices in the same story.
ChoiceScene choiceScene = new ChoiceScene("1");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));
// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
// This is an action listener. The code here is called when they click a choice.
Choice choice = event.getChoice(); // Gets the choice button they clocked.
if (choice.getId().equalsIgnoreCase("help")) {
// The player selected the help choice
// Lets say this decision isn't important and it only ahieves a couple of lines of unique dialogue.
// We can skip down to that specific part of the dialogue.
displayScene("3");
} else if (choice.getId().equalsIgnoreCase("run")) {
// The player decided to run away
displayScene("2");
}
});
addScene(new ImageScene("2", mc, "I can't risking my life here. I've put everything on the line. I must live.")
.onEnd(event -> {
// When this event ends we do not want it to play the dialogue that shows when they saved the women.
event.setAutoPlayNextScene(false); // This is very important!!! Stories will automatically run the game and play the next scene. We need to disable this behavior to properly display the scenes.
displayScene("4");
}));
addScene(new ImageScene("3", mc, "Here, take my hand. We must hurry!")
.onEnd(event -> {
event.setAutoPlayNextScene(false);
displayScene("4");
}));
addScene(new ImageScene("4", mc, "I got out, though barely. That was a lot closer than I ever wanted to."));This is how you would do everything in a separate story. I highly recommend this method especially for complex and important decisions. Simple ones that only achieve a different string of dialogue are easier the other way.
ChoiceScene choiceScene = new ChoiceScene("1");
choiceScene.addChoice(new Choice("help", "Help the injured women."));
choiceScene.addChoice(new Choice("run", "Save yourself."));
// To control how the story takes place depending on the action they chose
choiceScene.onChoice(event -> {
// This is an action listener. The code here is called when they click a choice.
Choice choice = event.getChoice(); // Gets the choice button they clocked.
if (choice.getId().equalsIgnoreCase("help")) {
// The player selected the help choice
SaveCompanionStory saveStory = (SaveCompanionStory) RenJava.getInstance().getPlayer().getStory("save-companion");
saveStory.start();
} else if (choice.getId().equalsIgnoreCase("run")) {
// The player decided to run away
LeaveCompanionStory leaveStory = (LeaveCompanionStory) RenJava.getInstance().getPlayer().getStory("leave-companion");
leaveStory.start();
}
});You can chain stories so they play after each-other. You can do this by calling the .start() function inside the scene events. Or can you do so by mapping the stories after each one.
// This is the final scene in the story. Once it ends play the next story
addScene(new ImageScene("4", mc, "I got out, though barely. That was a lot closer than I ever wanted to.")
.onEnd(event -> {
NextStory nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
nextStory.start();
}));To map stories in a specific order.
public class MyStory extends Story {
public MyStory() {
super("my-story");
onEnd(event -> {
// This is called when the story has reached its end.
// Call next story
NextStory nextStory = RenJava.getInstance().getPlayer().getStory("next-story");
nextStory.start();
});
}
}Like said previously, the image scene is your standard VN scene. It contains an image and character dialogue. Note, both the image and dialogue can have null values.

Below is the current API documentation which explains some of the parameters needed to create an ImageScene. All RenScene's need to be in a story to be properly rendered and handled by the engine.
/**
* Creates an ImageScene object representing an image scene in the RenJava framework.
* An image scene is used to display an image and text associated with a character.
* Image scenes are typically used to present visuals and dialogue during gameplay or narrative progression.
*
* @param id The ID used to identify the scene.
* @param character The character who is talking. Pass null if no character is talking in the scene.
* @param dialogue The dialogue of the character. Pass null or an empty string if no one is talking.
* @param backgroundImage The background image loader for the scene.
*/
public ImageScene(String id, @Nullable Character character, String dialogue, @Nullable ImageOverlay backgroundImage) {}Some of the parameters can be handled to accept null. Character and ImageOverlay can be null.
public void init() {
// The Character parameter is null which means the textbox nor the dialogue will be rendered.
addScene(new ImageScene("1", null, "Dialogue", new ImageOverlay("image.png")));
// The image is null which will result in a black box being rendered instead.
addScene(new ImageScene("2", character, "Dialogue", null));
// The image was not specified so the engine will use the previous valid image.
addScene(new ImageScene("1", null, "Dialogue"));
}UNDER CONSTRUCTION! ALL SCENES AND DOCUMENTATION WILL BE ADDED HERE SOON.