Turn-Based RPG

Материал из Поле цифровой дидактики

A turn-based RPG is a form of role-playing game. In turn-based RPGs, battles consist of turns where a player can command their characters to perform various actions to defeat opponents. Turn-based RPGs are one of the most popular types of RPGs on gaming consoles, but are not very popular as Scratch projects.

This article contains a guide on how to create a turn-based system for an RPG.

When finished, the project should look something like this.

Game Statistics

Turn based RPG battles focus mainly on statistics and formulas. In this tutorial the player has the following statistics:

Atk: Physical attack power

Def: Physical defense

MaxHP: Most HP (Health Points) the character can have

HP: Health points, the player is dead when this is at 0

MaxMP: Most MP (Magic Points) a character can have

MP: Magic points, used for magic attacks

MGAtk: Magic attack power

MGDef: Defense from magic attacks

Additionally, the player character has statistics for their weapons:

Atk: A set increase in attack power P: A random increase in attack power

An enemy has the same statistics as the player character except without the extra statistics for weapons and Magic Points.

The first thing you should do is create the statistic variables. Variables belonging to the player character should start with "C_" those belonging to the player character's weapon should start with "C_Wep" and those belonging to the enemy should start with "E_".

We are also going to gives the variables some test values when the green flag is clicked so that we may properly play the game during testing. When finished, your set of variables should look something like this:

When flag clicked
set [C_Atk v] to [5]
set [C_Def v] to [5]
set [C_MGAtk v] to [15]
set [C_MGDef v] to [5]
set [C_MaxMP v] to [100]
set [C_MP v] to (C_MaxMP)
set [C_MaxHP v] to [100]
set [C_HP v] to (C_MaxHP)


When flag clicked
set [C_Wep_Atk v] to [5]
set [C_Wep_P v] to [5]


When flag clicked
set [E_Atk v] to [5]
set [E_Def v] to [10]
set [E_MaxHP v] to [120]
set [E_HP v] to (E_MaxHP)
set [E_MGAtk v] to [0]
set [E_MGDef v] to [0]

Battle Formulas

Next you will learn the formulas for the different attacks. The variables you have just created fit snugly into those formulas. The formulas we will use are based on a classic RPG called Super Mario RPG.

Player Melee Attack Damage Formula:

C_Atk(C_Wep_Atk+(pick random (0-C_Wep_P) to (C_Wep_P))-E_Def)

(((C_Atk) + ((C_Wep_Atk) + (pick random ((0) - (C_Wep_P)) to (C_Wep_P)))) - (E_Def))

Player Magic Attack Damage Formula:

C_MGAtk+"spellrating"-E_MGDef

(((C_Atk) + (22)) - (E_MGDef))
  • Each spell has a strength rating which must be set.


Enemy Melee Attack Damage Formula:

E_Atk-C_Def

((E_Atk) - (C_Def))

Enemy Magic Attack Damage Formula:

E_MGAtk+"spellrating"-C_MGDef

(((E_MGAtk) + (0)) - (C_MGDEF))
  • Each enemy spell has a strength rating which must be set.

You need to build these scripts and leave them on the stage. They will be used as parts of the scripts for the battle actions.

Programming Actions

Now that you know how the game will handle formulas and stats, it is time to allow the user to play a simple RPG game.

To control the game, we need a few variables that serve to make the game run as opposed to building the battle formulas.

You will now need to create the following variables:

dam: Stores the results of the damage calculations

gamestate: Tells the game what is currently supposed to be doing e.g. letting the player pick attacks, or letting the enemy character attack

  • The number of the gamestate variable tells the program different things. This is the list of values for the variable:

1. Wait for player to enter a command

2. Wait for player character animation to finish

3. Allow the enemy to pick an action and carry it out

0. The RPG battle is over

C_Stance: Tells the character sprite which animation to play e.g. standing, attacking, cast spell.


E_Stance: Tells the enemy sprite which animation to play e.g. standing, attacking, cast spell.


  • The stance variables tell the which animations to play, for this game we will make the following animations:
  1. Idle animation
  2. Attack animation 1 (melee)
  3. Attack animation 2 (spell)
  4. Hurt animation
  5. Death animation


You will also need the following Broadcasts:

melee: Makes the player character use a melee attack

spell: Makes the player character do a magic attack

emelee: Makes the enemy character use a melee attack

espell: Makes the enemy character do a magic attack

stance: Tells the player sprite to do an animation

enemy_stance: Tells the enemy sprite to do an animation

Now, create the following scripts on the stage

when I receive [melee v]
set [C_Stance v] to [2]
set [gamestate v] to [2]
broadcast [stance v] and wait
set [dam v] to (((C_Atk) + ((C_Wep_Atk) + (pick random ((0) - (C_Wep_P)) to (C_Wep_P)))) - (E_Def))
if <(dam) < (0)> then
set [dam v] to (0)
end
change [E_HP v] by ((-1) * (dam))
set [E_Stance v] to [4]
broadcast [E_stance v] and wait
set [gamestate v] to [3]
set [C_Stance v] to [1]
broadcast [stance v]


when I receive [spell v]
set [C_Stance v] to [3]
set [gamestate v] to [2]
broadcast [stance v] and wait
set [dam v] to (((C_Atk) + (22)) - (E_MGDef))
if <(dam) < (0)> then
set [dam v] to [0]
end
change [E_HP v] by ((-1) * (dam))
set [E_Stance v] to [4]
broadcast [E_stance v] and wait
set [gamestate v] to [3]
set [C_Stance v] to [1]
broadcast [stance v]

when I receive [emelee v]
set [E_Stance v] to [2]
broadcast [E_stance v] and wait
set [dam v] to ((E_Atk) - (C_Def))
if <(dam) < (0)> then
set [dam v] to [0]
end
change [C_HP v] by ((-1) * (dam))
set [C_Stance v] to [4]
broadcast [stance v] and wait
set [E_Stance v] to [1]
broadcast [E_stance v] and wait
set [gamestate v] to [1]


when I receive [espell v]
set [E_Stance v] to [3]
broadcast [E_stance v] and wait
set [dam v] to (((E_MGAtk) + (0)) - (C_MGDef))
if <(dam) < (0)> then
set [dam v] to [0]
end
change [C_HP v] by ((-1) * (dam))
set [C_Stance v] to [4]
broadcast [stance v] and wait
set [E_Stance v] to [1]
broadcast [E_stance v] and wait
set [gamestate v] to [1]


You will also need a way to allow the player to broadcast these actions so make 2 buttons with the following scripts:


Melee Button

when this sprite clicked
if <(gamestate) = (1)> then
broadcast [melee v]
end

when flag clicked
forever
if <(gamestate) = (1)> then
show
else
hide
end


Spell Button

when this sprite clicked
if <<(gamestate) = (1)> and <<(C_MP) > (15)> or <(C_MP) = (15)>>> then
broadcast [spell v] and wait
change [mp v] by (-15)
end


when flag clicked
forever
if <(gamestate) = (1)> then
show
else
hide
end


The player should only be able to press these buttons when the gamestate variable is at 1 so we hide them if the variable is not at one. The buttons work in similar ways but the spell button check to see if the player has enough MP and only make the broadcast if there is enough MP.


As a finishing touch we are going to set the variables that are not involved in the battle formulas to a default value.

when flag clicked
switch backdrop to [background1 v]
set [gamestate v] to [1]
set [C_Stance v] to [1]
set [E_Stance v] to [1]
broadcast [stance v]
broadcast [E_stance v]

Character Animation

You will need an animation for each stance, you can either draw them yourself, download a premade sprite sheet, or use a sprite generator such as the Charas-Project Generator

For this project, the article will use the Charas generator for quick and easy sprites but those who are more artistically inclined can draw them. If you are not drawing sprite then you must import the sprite sheets and cut the sprite out. For a quick guide on how to do this go to this forum topic.

The animations that you need are the following:

  1. Idle animation
  2. Attack animation 1 (melee)
  3. Attack animation 2 (spell)
  4. Hurt animation
  5. Death animation

To make the character sprites to play a little animation every time the stance broadcasts are called so we need a script like this one. The blocks inside the if blocks are for the animations are can be customized in any way you like.

when I receive [stance v]
if <(C_HP) < [1]> then
switch costume to [dead_1 v]
else
if <(C_Stance) = [1]> then
switch costume to [idle v]
end
if <(C_Stance) = [2]> then
switch costume to [idle v]
move (100) steps
switch costume to [melee_1 v]
wait (0.1) secs
switch costume to [melee_2 v]
wait (0.1) secs
switch costume to [melee_3 v]
wait (0.1) secs
go to x: (-90) y: (-89)
switch costume to [idle v]
end
if <(C_Stance) = [3]> then
switch costume to [idle v]
move (100) steps
switch costume to [spell_1 v]
wait (1) secs
go to x: (-90) y: (-89)
switch costume to [idle v]
end
if <(C_Stance) = [4]> then
say (dam)
switch costume to [hurt_1 v]
repeat (3)
move (-10) steps
end
repeat (3)
move (10) steps
end
wait (0.5) secs
switch costume to [idle v]
say []
end
end


The script for the enemy sprite will look the same except that the broadcast will be the enemy's broadcast (E_stance) and the stance variable will belong to the enemy (E_stance).

A picture of the enemy script can be seen below.

when I receive [E_stance v]
if <(E_HP) < [1]> then
switch costume to [dead_1 v]
else
if <(E_Stance) = [1]> then
switch costume to [idle v]
end
if <(E_Stance) = [2]> then
switch costume to [idle v]
move (100) steps
switch costume to [melee_1 v]
wait (0.1) secs
switch costume to [melee_2 v]
wait (0.1) secs
switch costume to [melee_3 v]
wait (0.1) secs
go to x: (90) y: (-89)
switch costume to [idle v]
end
if <(E_Stance) = [3]> then
switch costume to [idle v]
move (100) steps
switch costume to [spell_1 v]
wait (1) secs
go to x: (90) y: (-89)
switch costume to [idle v]
end
if <(E_Stance) = [4]> then
say (dam)
switch costume to [hurt_1 v]
repeat (3)
move (-10) steps
end
repeat (3)
move (10) steps
end
wait (0.5) secs
switch costume to [idle v]
say []
end
end

Automating the Enemy

Since the enemy does not have a player to control its actions, it will do so on its own.

Build this script on the enemy sprite and it should attack the player by itself when gamestate is equal to 3.

when flag clicked
forever
if <(gamestate) = [3]> then
if <(E_HP) < [1]> then
broadcast [win_battle v] and wait
end
if <(gamestate) = [3]> then
set [randAtk v] to (pick random (1) to (2))
if <(randAtk) = [1]> then
broadcast [emelee v] and wait
end
if <(randAtk) = [2]> then
broadcast [espell v] and wait
end
if <(C_HP) < [1]> then
broadcast [lose_battle v] and wait
end
end
end


This script as you can see also is able to call the broadcasts that end the game, the win_battle and lose_battle broadcasts.

Ending the Game

There are two conditions in which the game ends:

  1. The player loses all HP and loses the game
  2. The enemy character loses all HP and the player wins

As you can see from the above script that automates the enemy, the enemy checks to see if these conditions have been fulfilled and calls the appropriate broadcast.

In the demo project, a different background plays and the gamestate is set to 0, which means that the battle sequence is done.

when I receive [lose_battle v]
set [gamestate v] to [0]
switch backdrop to [background3 v]


when I receive [win_battle v]
set [gamestate v] to [0]
switch backdrop to [background2 v]

Finishing Remarks

Test your project out, if you followed this tutorial correctly, then you should have a working RPG project. If it is not working then it is likely you did not correctly copy the scripts in this article. The best way to troubleshoot is to compare it with the example one given at the start of this article.

See Also