/unity3d-levelup

Unity3D F2P game progression library - worlds, levels, missions, scores, records and more. Part of The SOOMLA Framework - for game design, economy modeling and faster development.

Primary LanguageC#Apache License 2.0Apache-2.0

Looking for great devs who want maintain and moderate this project !!

If you want to take over... contact us at os@soom.la

This project is a part of The SOOMLA Framework, which is a series of open source initiatives with a joint goal to help mobile game developers do more together. SOOMLA encourages better game design, economy modeling, social engagement, and faster development.

unity3d-levelup

unity3d-levelup is a library built for easily modeling game structure and user progression, and allows rapid protoyping using a standard and simplified model. It acts as sort of a 'blueprint' for the game, modeling worlds/levels, gates to levels, missions and rewards that can be completed and achieved. All this is backed by SOOMLA's core tools, and can be easily integrated with more SOOMLA modules, like unity3d-store for IAP, or unity3d-profile for social related functions.

unity3d-levelup is the implementation of the LevelUp module for Unity3d.

Contents

Model Overview

Generally, the SOOMLA sources contain detailed documentation on the different entities and how to use them, but here's a quick glance:

SOOMLA's LevelUp Model

World / Level

A Level is pretty clear, and most games have them. A simple example is an Angry Birds single level, where you need to knock out all the pigs. It measures specific things, such as duration it takes to complete, and can be started and ended.

A World is a more general concept than a Level (a Level Is-a World), and can have innerWorlds to create hierarchies. Another example from Angry Birds is level pages and episodes, which contain the actual levels.

The Initial World is a container world for all worlds and levels in the game. We use the Initial World to intialize the LevelUp module.

Score

A Score is something which can be accumulated or measured within a World (or Level of course). It can be incremented or decremented based on user actions, and recorded at the completion of the World / Level.

This, in turn, can later be applied to high scores or best times, or treated as collectibles that can be awarded upon completion.

Gate

A Gate is a closed portal from one World to the next. It can be unlocked in many different ways (according to Gate type), and can also be combined into a GatesList to build more complex Gates.

Mission/Challenge

A Mission is a single task a player can complete in a game, usually for a Reward.

A Challenge is a set of Missions that need to be completed, so it's a big Mission built out of several smaller Missions.

Reward

A Reward is some kind of perk or status a player can achieve in the game. This can be either a badge, a virtual item from the game's economy (sword, coins etc.) or anything you can think of, really (unlocking game content or levels comes to mind).

Getting Started

NOTE: LevelUp depends on SOOMLA's other modules: Core, Store, and Profile. This document assumes that you are new to SOOMLA and have not worked with any of the other SOOMLA modules. If this is not the case, and you already have some or all of the other modules, please follow these directions only for the modules you are missing and of course, for the LevelUp module.

  1. Clone this repository recursively: git clone --recursive https://github.com/soomla/unity3d-levelup.git

  2. Run ./build_all from project directory

  3. You can work with sources, or if you run deploy/create_deploys, it will create packages that you can use in your project.

  4. On the menu bar click "Window -> Soomla -> Edit Settings" and change the value for "Soomla Secret" (also setup Public Key if you're building for Google Play):

    • Soomla Secret - is an encryption secret you provide that will be used to secure your data. (If you used versions before v1.5.2 this secret MUST be the same as Custom Secret)
      Choose the secret wisely. You can't change it after you launch your game!
    • Public Key - is the public key given to you from Google. (iOS doesn't have a public key).
  5. Create your own Initial World which should contain all the 'blueprint' of the game (see Model Overview). Initialize LevelUp with the class you just created:

    SoomlaLevelUp.Initialize (initialWorld);

    Initialize LevelUp ONLY ONCE when your application loads.

    Initialize LevelUp in the "Start()" function of a 'MonoBehaviour' and NOT in the "Awake()" function. SOOMLA has its own 'MonoBehaviour' and it needs to be "Awakened" before you initialize.

  6. You'll need an event handler in order to be notified about LevelUp related events. refer to the Event Handling section for more information.

And that's it ! You have game architecture capabilities at your fingertips.

Using unit tests

To use SOOMLA unit tests in the Other Settings panel of the Player Settings, you will see the Scripting Define Symbols, enter the name “SOOMLA_TEST”. If you are using Unity version older 5.3.0, download NUnit and import to your project.

Integration with SOOMLA unity3d-store

Please follow the steps in unity3d-store for the Store part of the setup. Then, you can use the store-related LevelUp classes, such as VirtualItemScore or VirtualItemReward or BalanceGate.

Integration with SOOMLA unity3d-profile

Please follow the steps in unity3d-profile for the Profile part of the setup. Then, you can use the profile-related LevelUp classes, such as SocialLikeMission.

Event Handling

SOOMLA lets you subscribe to LevelUp events, get notified and implement your own application specific behavior to those events.

Your behavior is an addition to the default behavior implemented by SOOMLA. You don't replace SOOMLA's behavior.

The Events class is where all event go through. To handle various events, just add your specific behavior to the delegates in the Events class.

For example, if you want to 'listen' to a WorldCompleted event:

LevelUpEvents.OnWorldCompleted += onWorldCompleted;

public void onWorldCompleted(World world) {
    Debug.Log("The world " + world.ID + " was COMPLETED!");
}

One thing you need to make sure is that you instantiate your EventHandler before LevelUp.
So if you have:

private static Soomla.Example.ExampleEventHandler handler;

you'll need to do:

handler = new Soomla.Example.ExampleEventHandler();

before

Soomla.Levelup.SoomlaLevelUp.Initialize (initialWorld);

Debugging

If you want to see full debug messages from android-levelup and ios-levelup you just need to check the box that says "Debug Messages" in the SOOMLA Settings. Unity debug messages will only be printed out if you build the project with Development Build checked.

Example Usages

Examples using virtual items are dependent on unity3d-store module, with proper SoomlaStore initialization and IStoreAssets definitions. See the unity3d-store integration section for more details.

  • Mission with Reward (collect 5 stars to get 1 mega star)

    VirtualItemReward virtualItemReward = new VirtualItemReward("mega_star_reward_id",
       "MegaStarReward", megaStarItemId, 1);
    
    List<Reward> rewards = new List<Reward>();
    rewards.Add(virtualItemReward);
    
    BalanceMission balanceMission = new BalanceMission("star_balance_mission_id",
       "StarBalanceMission", rewards, starItemId, 5);
    
    // use the store to give the items out, usually this will be called from in-game events
    // such as player collecting the stars
    StoreInventory.GiveItem(starItemId, 5);
    
    // events posted:
    // 1. OnGoodBalanceChanged (Store events)
    // 2. OnMissionCompleted (LevelUp events)
    // 3. OnRewardGivenEvent (Core events)
    
    // now the mission is complete, and reward given
    balanceMission.IsCompleted(); // true
    virtualItemReward.Owned; // true
  • RecordGate with RangeScore

    Level lvl1 = new Level("lvl1_recordgate_rangescore");
    Level lvl2 = new Level("lvl2_recordgate_rangescore");
    
    string scoreId = "range_score";
    RangeScore rangeScore = new RangeScore(scoreId, new RangeScore.SRange(0.0, 100.0));
    
    string recordGateId = "record_gate";
    RecordGate recordGate = new RecordGate(recordGateId, scoreId, 100.0);
    
    lvl1.AddScore(rangeScore);
    
    // Lock level 2 with record gate
    lvl2.Gate = recordGate;
    
    // the initial world
    world.AddInnerWorld(lvl1);
    world.AddInnerWorld(lvl2);
    
    SoomlaLevelUp.Initialize(world);
    
    lvl1.Start();
    
    // events posted:
    // OnLevelStarted (LevelUp events)
    
    rangeScore.Inc(100.0);
    
    lvl1.End(true);
    
    // events posted:
    // OnLevelEnded (LevelUp events)
    // OnWorldCompleted (lvl1) (LevelUp events)
    // OnGateOpened (LevelUp events)
    // [OnScoreRecordReached] - if record was broken (LevelUp events)
    
    recordGate.IsOpen(); // true
    
    lvl2.CanStart(); // true
    lvl2.Start();
    lvl2.End(true);
    
    // events posted:
    // OnWorldCompleted (lvl2) (LevelUp events)
    
    lvl2.IsCompleted(); // true
  • VirtualItemScore

    Level lvl1 = new Level("lvl1_viscore");
    string itemId = ITEM_ID_VI_SCORE;
    string scoreId = "vi_score";
    VirtualItemScore virtualItemScore = new VirtualItemScore(scoreId, itemId);
    lvl1.AddScore(virtualItemScore);
    
    world.AddInnerWorld(lvl1);
    
    SoomlaLevelUp.Initialize(world);
    
    lvl1.Start();
    // events posted:
    // OnLevelStarted (LevelUp events)
    
    virtualItemScore.Inc(2.0);
    // events posted:
    // OnGoodBalanceChanged (Store events)
    
    lvl1.End(true);
    // events posted:
    // OnLevelEnded (LevelUp events)
    // OnWorldCompleted (lvl1) (LevelUp events)
    // [OnScoreRecordChanged] - if record was broken (LevelUp events)
    
    int currentBalance = StoreInventory.GetItemBalance(ITEM_ID_VI_SCORE);
    // currentBalance == 2
  • Challenge (Multi-Mission)

    string scoreId = "main_score";
      Score score = new Score(scoreId);
    
      Mission mission1 = new RecordMission("record1_mission", "Record 1 mission",
                                           scoreId, 10.0);
      Mission mission2 = new RecordMission("record2_mission", "Record 2 mission",
                                           scoreId, 100.0);
      List<Mission> missions = new List<Mission>() { mission1, mission2 };
    
      BadgeReward badgeReward = new BadgeReward("challenge_badge_reward_id",
                                                "ChallengeBadgeRewardId");
      List<Reward> rewards = new List<Reward>() { badgeReward };
    
      Challenge challenge = new Challenge("challenge_id", "Challenge", missions, rewards);
    
      challenge.IsCompleted(); //false
    
      World world = new World("initial_world");
      world.AddMission(challenge);
      world.AddScore(score);
    
      SoomlaLevelUp.Initialize(world);
    
      score.SetTempScore(20.0);
      score.Reset(true);
    
      // events:
      // OnMissionCompleted (mission1) (LevelUp events)
      // [OnScoreRecordReached] - if record is broken
    
      score.SetTempScore(120.0);
      score.Reset(true);
    
      // events:
      // OnMissionCompleted (mission2) (LevelUp events)
      // OnMissionCompleted (challenge) (LevelUp events)
      // OnRewardGivenEvent (badgeReward) (Core events)
    
      challenge.IsCompleted(); // true
      badgeReward.Owned; // true
  • GatesList

Note that currently a GatesList gate is automatically opened when sub-gates fulfill the GatesList requirement.

string recordGateId1 = "gates_list_record_gate_id1";
string scoreId1 = "gates_list_score_id1";
double desiredRecord1 = 10.0;
string recordGateId2 = "gates_list_record_gate_id2";
string scoreId2 = "gates_list_score_id2";
double desiredRecord2 = 20.0;

Score score1 = new Score(scoreId1);
Score score2 = new Score(scoreId2);

World world = new World("initial_world");
Level lvl1 = new Level("level1_id");
lvl1.AddScore(score1);
lvl1.AddScore(score2);
world.AddInnerWorld(lvl1);

RecordGate recordGate1 = new RecordGate(recordGateId1, scoreId1, desiredRecord1);
RecordGate recordGate2 = new RecordGate(recordGateId2, scoreId2, desiredRecord2);

List<Gate> gates = new List<Gate>() { recordGate1, recordGate2 };

GatesListOR gatesListOR = new GatesListOR("gate_list_OR_id", gates);

GatesListAND gatesListAND = new GatesListAND("gate_list_AND_id", gates);

SoomlaLevelUp.Initialize(world);

score1.SetTempScore(desiredRecord1);
score1.Reset(true);

recordGate1.IsOpen(); // true
gatesListOR.IsOpen(); // true

gatesListAND.CanOpen(); // false (all sub-gates need to be open for AND)
gatesListAND.IsOpen(); // false

score2.SetTempScore(desiredRecord2);
score2.Reset(true);

recordGate2.IsOpen(); // true
gatesListOR.IsOpen(); // still true
gatesListAND.IsOpen(); // true

Contribution

SOOMLA appreciates code contributions! You are more than welcome to extend the capabilities of SOOMLA.

Fork -> Clone -> Implement -> Add documentation -> Test -> Pull-Request.

IMPORTANT: If you would like to contribute, please follow our Documentation Guidelines. Clear, consistent comments will make our code easy to understand.

SOOMLA, Elsewhere ...

License

Apache License. Copyright (c) 2012-2014 SOOMLA. http://www.soom.la