2-Player Split Screen Effect?

PK8

I sense there's something in the wind...
Veteran
Joined
Mar 17, 2012
Messages
1,220
Reaction score
152
Primarily Uses
Heheh, kind of funny. You know... a scripter... requesting a script. Heh.

Jokes aside, I was wondering if anybody wants to fulfill a request of mine. A split screen versus mode was something I had in mind for this project I'm working on, but I don't quite have the knowhow when it comes to implementing that.

I want to let players have the option to decide between splitting the screen horizontally or vertically, and to have both players share the same map.

And maybe a variable I could use to refer to the second player.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,072
First Language
English
I tried to implement something like this but couldn't figure out how to nicely split the screen without duplicating code.

SceneManager would need to be rewritten so that each player has their own scene managers, same with data manager and battle manager. After that I looked into the spriteset viewports so that I could cut it in half, and then decide where the origin was based on which "player" they were (eg: left/right for 2-player, 4 corners for 4-player), and use an updated Graphics module to return the appropriate width/height

But it didn't work out. Couldn't figure out how the map was drawn at all.

I've seen a couple implementations but they basically re-wrote the entire codebase by copying everything and creating "2p" versions.
 
Last edited by a moderator:

deilin

Ranger/Elementalist
Veteran
Joined
Mar 13, 2012
Messages
1,189
Reaction score
173
First Language
English
is there a 2p option for ace?
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,072
First Language
English
Not by default but you can just define a new set of keys for a new player and then have both players on the screen at the same time. Then you effectively have 2p, except any event triggers would affect both players because everything still assumes 1p.
 

mikb89

Villager
Member
Joined
Apr 28, 2012
Messages
10
Reaction score
6
First Language
Italian
Primarily Uses
For a split screen, isn't enough to have just two spriteset. Events have to be handled twice. Messages and windows have to be resized. If you want the single player to be able to use his menu while the other player walks around, you also have to put every menu on map or have two different scenes running.

We can avoid the last part, but it isn't good that player 1 can't move if player 2 is talking to an event. And also changing map for a single player, isn't too immediate.

It isn't difficult to do (well, a bit xD) but there are many things to rewrite. And it will be easily incompatible to whatever.

So it depends of what you want to sacrifice and what you need to have.

I suggest you, since you're a scripter, to try it yourself :)

Watch other split screen scripts, if you need. I developed my first split screen in XP (it came out ugly, but it impressed people anyway ^^) and my second in VX, which came out better, but before to make one in VX Ace I want to learn more, since I want it to be compatible, someway. Else nobody would use that and I'd wasted my time.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,072
First Language
English
How did you deal with global variables? Did you just duplicate them for player 2?
 
Last edited by a moderator:

mikb89

Villager
Member
Joined
Apr 28, 2012
Messages
10
Reaction score
6
First Language
Italian
Primarily Uses
Do you mean these?



Code:
    $game_temp		  = [Game_Temp.new, Game_Temp.new]
    $game_message	   = [Game_Message.new(0), Game_Message.new(1)]
    $game_system	    = [Game_System.new(0), Game_System.new(1)]
    $game_switches	  = Game_Switches.new
    $game_variables	 = Game_Variables.new
    $game_self_switches = Game_SelfSwitches.new
    $game_actors	    = Game_Actors.new
    $game_party		 = [Game_Party.new(0), Game_Party.new(1)]
    $game_troop		 = [Game_Troop.new(0), Game_Troop.new(1)]
    $game_map		   = []
    $game_map[0]	    = Game_Map.new(0)
    $game_map[1]	    = Game_Map.new(1)
I made an array when needed and changed the various Game_ to accept a parameter that says which player refers to.

This way more than two players could be simply added.

To paste just an example, the Game_System became like this:



Code:
class Game_System
  # [...]
  def initialize(p)
    # [...]
    @p = p
  end
  # [...]
  def update
    if @timer_working and @timer > 0
	  @timer -= 1
	  if @timer == 0 and $game_temp[@p].in_battle	 # If the timer 0 in battle
	    $game_temp[@p].next_scene = "map"			 # interrupt the battle
	  end
    end
  end
end
Then the major code is on the map classes.

But I don't really like this system :/ I was searching for something better.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,072
First Language
English
Do you think there is a way to make it so that you can run two separate instances of the game (eg: they have their own global variables and everything) in one game.exe instance?

Here is my solution to the windows and viewports.

I approach it by simply offsetting the position based on the current scene. It can be extended to arbitrary number of players given the appropriate calculations, but it is not useful for more than 2 players because the resolution is just too small. Until we find a way to make it at least 1024x768 or something it is not feasible to have split screen for multiplayer. But DS-style split screen controls is fine.





Code:
# assumes a vertical split

module SceneManager

  class << self
    attr_reader :id     #which instance of the game is this? left or right
  end

  def self.run(id=0)
    @id = id
    tsuki_run(id)
  end
end

module Graphics

  class << self
    alias :th_split_screen_width :width
  end

  def self.width
    return th_split_screen_width / 2
  end
end

class Viewport

  alias :th_split_screen_init :initialize
  def initialize(*args)
    if args.count == 0
      x = SceneManager.id * Graphics.width
      th_split_screen_init(x, 0, Graphics.width, Graphics.height)
    elsif args.count == 4
      th_split_screen_init(*args)
    else
      th_split_screen_init(*args)
    end
  end
end

class Window

  alias :th_split_screen_init :initialize
  def initialize(x, y, width, height)
    x += SceneManager.id * Graphics.width
    th_split_screen_init(x, y, width, height)
  end
end
Start the game like this



Code:
# if you want to be on the left side
rgss_main { SceneManager.run(0) }

# if you want to be on the right side
rgss_main { SceneManager.run(1) }
This means I don't have to write new windows for each screen; I just need to write a single window and let the offsets do their job.

However, ignoring two-player gaming, I can use this to achieve a player-switch game where you have to switch between actors in two screens, and coordinate them as such.

I think that is a more realistic goal at the moment rather than trying to get two completely different players running at the same time.
 
Last edited by a moderator:

mikb89

Villager
Member
Joined
Apr 28, 2012
Messages
10
Reaction score
6
First Language
Italian
Primarily Uses
I like the method you're using!

And VX Ace handles menus' sizes automatically, thing that VX didn't do, so everything is adapted without editing, once you specific the size.

The same thing should be done for Sprite, right? Or mmh... maybe this will depend on Sprite object having a Viewport set or not.

For the rgss_main mh, could Fiber be used? I don't know very well how they work, really, but I remember they are used to let multiple tasks work.

I made this:



Code:
class Scene_Base
  alias :mik_update_basic :update_basic
  def update_basic
    mik_update_basic
    Fiber.yield
  end
end

fib1 = Fiber.new do
  rgss_main { SceneManager.run(0) }
end

fib2 = Fiber.new do
  rgss_main { SceneManager.run(1) }
end

loop do
  p 1; fib1.resume
  p 2; fib2.resume
end
(it could be simplified with an array, and Fiber should resume only if fib.alive?)

This way both main will be updated (you'll see on the console 1-2-1-2-1-2...), but it still doesn't work, global variables have to be duplicated.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,230
Reaction score
3,072
First Language
English
One of the scenes is disposed immediately. Is this because we only have reference to one of the maps (eg: $game_map) and so the other is eliminated? I'm not sure if there are actually two instances of SceneManager or just one. We might have to make it so that there are two.

For global variables, I was thinking maybe we can do something like

1: change DataManager to a class, and have each instance create their own DataManager

2: replace all references to $game_... and change them to DataManager method calls, such as DataManager.player.____

DataManager holds references to @game_player, @game_map, etc. which is basically the same as what $game_player and $game_map were for and then we don't need to duplicate code at all.

We would still keep certain global variables, but they have to be updated to be thread-safe. For example, $game_actors might be shared between both instances. You can't have duplicate actors, but each player can choose who they want in their party.

I don't mind breaking compatibility with every script if we have to; I just don't want duplicate code, nor do I want to hardcode anything like indices. DataManager and SceneManager should handle all instance-specific details internally.

....

Another (very hack-ish, might not be possible) way is to possibly change object access.

For example, suppose I define



Code:
class Game_Players

   def initialize
      @data = [Game_Player.new, Game_Player.new]
   end

   def .
      return @data[SceneManager.id]
   end
end
This means that everytime we say



Code:
$game_player.do_this
It actually is an array access and returning a specific Game_Player object.

LOL that would be so bad, but it would make testing really simple...

....

For sprites, they are just specified using their x,y coordinates and is based on the viewport they are drawn in. Which means you basically don't have to touch them! An example is the sprites on the map (Sprite_Character)

As for VX...



Code:
def initialize(actor)
  super(0, 0, 544, 416) # lol
  @actor = actor
  refresh
end
EDIT:

lol how about this for some quick testing



Code:
class Game_Players

  def initialize
    @data = [Game_Player.new, Game_Player.new]
  end

  def method_missing(method)
    @data[SceneManager.id].send(method)
  end
end

$game_player = Game_Players.new
$game_player.do_something
# no method exception? Find the correct player to call the method on
I thought it was pretty amusing since our wrapper classes can just contain no real method definitions and so method_missing will almost always be invoked......

Note that this is not a serious solution. The fact that we're relying on exceptions to get things to work is just silly. However if it helps eliminate the global variable problem so that we can get on with the rest of the script it's not too bad...
 
Last edited by a moderator:

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Who loves the Final Fantasy series?~
Weather and time system twin release. Now with weather icons and possibility to add them in your custom clock!
Kingdom Under Fire: The Crusaders is finally being ported to PC. I loved playing that game on the original Xbox.
A friend told me to stop making nomnom or gulping noise when eating or drinking. Plot twist of my life. I thought people can't hear that!

Forum statistics

Threads
94,411
Messages
920,756
Members
124,206
Latest member
wilfpower
Top