06MA4010991AP
Computer Game Design
Week 9
Multiplayer Games
A multiplayer video game is one which more than one person can play in the same game environment at the same time. Which enables flexibility and ingenuity of regular human thinking, other than AI controlled opponents.
There are two types of multiplayer platforms:
1) Single System
- several controllers / take turns
- split screen
- console / arcade games
2) Networking among multiple Systems
- network interface / protocol
- architecture and latency
Multplayer design elements
1) Discovery
In most successful games, players uncover new elements as they progress; people want to find new depths at their own pace. This is easy to do online where you literally add to the host any time, as long as the architecture you have built allows it. Special scenarios can be organized by the company or by players themselves.
2) Amount of winners and losers
Games where I win and you lose are bad. Worse still is "I win and all the rest of you lose". Notwithstanding the current cultural obsession with endzone strutting by winners, losers do not enjoy themselves and if you can help take the sting out of it, you should. Alliances, cooperative play, ranked "winners" rather than "A winner" with a bunch of losers are all options.
3) Personalization
Let players define their own icons that the others see or somehow personalize their own game space. A big part of the enjoyment of being with others is expressing yourself. A bunch of player avatars all dressed from the same menu gives me the creeps. Encourage graffiti.
4) Facilitate relationships
Allow players to form clubs, clans, groups and facilitate scheduled as well impromptu meetings online. Help strangers mix and friends find each other.
5) Time limits
Whenever possible design your game so it can be played within a fixed time limit. This will allow people to schedule their involvement. A game you can play a couple of times in an evening would be a good design goal. If you can't end the game at specific times try to at least facilitate a graceful exit opportunity such that a player quits while they are having fun and not after they're so exhausted they'll never come back again.
6) Game balance
Try to keep the distance between the losers and the winners small enough that the outcome is in doubt as long as possible. You can adjust random events, attrition factors or whatever. They'll thank you for keeping the games interesting even though you should probably not tell them what you're doing.
7) Handicapping
Let players handicap themselves if they want. Some players are willing to play with one hand behind their back so let them. (The most common use of this will be parents and kids playing together).
8) Persistence
Players want to have a permanent effect on the world, and this illusion helps suspend disbelief. The difficulty is in making an environment consistent yet not cluttered by debris from previous play, and/or hostile to players entering for the first time.
Simple multiplayer game
float p1_arrowX; float p1_arrowY; float p1_arrowAngle; float p1_arrowAcceleration; float p1_arrowSpeedX; float p1_arrowSpeedY; boolean p1_upPressed,p1_downPressed,p1_leftPressed,p1_rightPressed; float p2_arrowX; float p2_arrowY; float p2_arrowAngle; float p2_arrowAcceleration; float p2_arrowSpeedX; float p2_arrowSpeedY; boolean p2_upPressed,p2_downPressed,p2_leftPressed,p2_rightPressed; void setup() { size(800,600); smooth(); frameRate(30); p1_arrowX = width*0.25; p1_arrowY = height/2; p1_arrowAcceleration = 0; p1_arrowAngle = -PI/2; p1_arrowSpeedX = 0; p1_arrowSpeedY = 0; p2_arrowX = width*0.75; p2_arrowY = height/2; p2_arrowAcceleration = 0; p2_arrowAngle = -PI/2; p2_arrowSpeedX = 0; p2_arrowSpeedY = 0; } void draw() { background(0); p1_arrowAcceleration *= 0.99; p1_arrowSpeedX *=0.99; p1_arrowSpeedY *=0.99; p2_arrowAcceleration *= 0.99; p2_arrowSpeedX *=0.99; p2_arrowSpeedY *=0.99; if(p1_upPressed) { //p1_arrowAcceleration += (25 -arrowSpeed) * 0.1; p1_arrowAcceleration++; if(p1_arrowAcceleration >50) p1_arrowAcceleration = 50; p1_arrowSpeedX = p1_arrowSpeedX*0.99 + cos(p1_arrowAngle)*p1_arrowAcceleration*0.01; p1_arrowSpeedY = p1_arrowSpeedY*0.99 + sin(p1_arrowAngle)*p1_arrowAcceleration*0.01; } if(p1_downPressed) { //p1_arrowAcceleration += (-25 -arrowSpeed) * 0.1; p1_arrowAcceleration--; if(p1_arrowAcceleration <-50) p1_arrowAcceleration = -50; p1_arrowSpeedX = p1_arrowSpeedX*0.99 + cos(p1_arrowAngle)*p1_arrowAcceleration*0.01; p1_arrowSpeedY = p1_arrowSpeedY*0.99 + sin(p1_arrowAngle)*p1_arrowAcceleration*0.01; } if(p1_leftPressed) { p1_arrowAngle -= radians(5); } if(p1_rightPressed) { p1_arrowAngle += radians(5); } p1_arrowX += p1_arrowSpeedX; p1_arrowY += p1_arrowSpeedY; if(p1_arrowX<0) p1_arrowX += width; else if(p1_arrowX>=width) p1_arrowX -= width; if(p1_arrowY<0) p1_arrowY += height; else if(p1_arrowY>=height) p1_arrowY -= height; if(p2_upPressed) { //p1_arrowAcceleration += (25 -arrowSpeed) * 0.1; p2_arrowAcceleration++; if(p2_arrowAcceleration >50) p2_arrowAcceleration = 50; p2_arrowSpeedX = p2_arrowSpeedX*0.99 + cos(p2_arrowAngle)*p2_arrowAcceleration*0.01; p2_arrowSpeedY = p2_arrowSpeedY*0.99 + sin(p2_arrowAngle)*p2_arrowAcceleration*0.01; } if(p2_downPressed) { //p1_arrowAcceleration += (-25 -arrowSpeed) * 0.1; p2_arrowAcceleration--; if(p2_arrowAcceleration <-50) p2_arrowAcceleration = -50; p2_arrowSpeedX = p2_arrowSpeedX*0.99 + cos(p2_arrowAngle)*p2_arrowAcceleration*0.01; p2_arrowSpeedY = p2_arrowSpeedY*0.99 + sin(p2_arrowAngle)*p2_arrowAcceleration*0.01; } if(p2_leftPressed) { p2_arrowAngle -= radians(5); } if(p2_rightPressed) { p2_arrowAngle += radians(5); } p2_arrowX += p2_arrowSpeedX; p2_arrowY += p2_arrowSpeedY; if(p2_arrowX<0) p2_arrowX += width; else if(p2_arrowX>=width) p2_arrowX -= width; if(p2_arrowY<0) p2_arrowY += height; else if(p2_arrowY>=height) p2_arrowY -= height; pushMatrix(); translate(p1_arrowX,p1_arrowY); rotate(p1_arrowAngle); triangle(-10,-10,-10,10,15,0); popMatrix(); pushMatrix(); translate(p2_arrowX,p2_arrowY); rotate(p2_arrowAngle); triangle(-10,-10,-10,10,15,0); popMatrix(); } void keyPressed() { if(key == 'w') { p1_upPressed = true; } else if(key == 's') { p1_downPressed = true; } else if(key == 'a') { p1_leftPressed = true; } else if(key == 'd') { p1_rightPressed = true; } else if(keyCode == UP) { p2_upPressed = true; } else if(keyCode == DOWN) { p2_downPressed = true; } else if(keyCode == LEFT) { p2_leftPressed = true; } else if(keyCode == RIGHT) { p2_rightPressed = true; } } void keyReleased() { if(key == 'w') { p1_upPressed = false; } else if(key == 's') { p1_downPressed = false; } else if(key == 'a') { p1_leftPressed = false; } else if(key == 'd') { p1_rightPressed = false; } else if(keyCode == UP) { p2_upPressed = false; } else if(keyCode == DOWN) { p2_downPressed = false; } else if(keyCode == LEFT) { p2_leftPressed = false; } else if(keyCode == RIGHT) { p2_rightPressed = false; } } |
import processing.net.*; Server myServer; PFont font; String sendingString; void setup() { size(600, 400); background(0); myServer = new Server(this, 10002); // Starts a myServer on port 10002 font = loadFont("ScalaSans-Caps-32.vlw"); sendingString = ""; } void draw() { background(0); Client c = myServer.available(); if(c != null) { println("Client said: "+c.readString()); } textFont(font); text(sendingString,10,height/2); } void keyPressed() { if(keyCode == ENTER) { println("Server said: "+sendingString); myServer.write(sendingString); sendingString = ""; } else if(keyCode == BACKSPACE && sendingString.length()>0) { sendingString = sendingString.substring( 0,sendingString.length()-1); } else if(key != CODED) { sendingString += key; } } |
import processing.net.*; Client c; PFont font; String sendingString; void setup() { size(600, 400); background(0); c = new Client(this, "127.0.0.1", 10002); font = loadFont("ScalaSans-Caps-32.vlw"); sendingString = ""; } void draw() { background(0); if(c.available() >0) { println("Server said: "+c.readString()); } textFont(font); text(sendingString,10,height/2); } void keyPressed() { if(keyCode == ENTER) { println("Client said: "+sendingString); c.write(sendingString); sendingString = ""; } else if(keyCode == BACKSPACE && sendingString.length()>0) { sendingString = sendingString.substring( 0,sendingString.length()-1); } else if(key != CODED) { sendingString += key; } } |
About port numbers
/** * Shared Drawing Canvas (Server) * by Alexander R. Galloway. * * A server that shares a drawing canvas between two computers. * In order to open a socket connection, a server must select a * port on which to listen for incoming clients and through which * to communicate. Once the socket is established, a client may * connect to the server and send or receive commands and data. * Get this program running and then start the Shared Drawing * Canvas (Client) program so see how they interact. */ import processing.net.*; Server s; Client c; String input; int data[]; void setup() { size(450, 255); background(204); stroke(0); smooth(); frameRate(10); // Slow it down a little s = new Server(this, 10002); // Start a simple server on a port } void draw() { if (mousePressed == true) { // Draw our line stroke(255); line(pmouseX, pmouseY, mouseX, mouseY); // Send mouse coords to other person s.write(pmouseX + " " + pmouseY + " " + mouseX + " " + mouseY + "\n"); print(pmouseX + " " + pmouseY + " " + mouseX + " " + mouseY + "\n"); } // Receive data from client c = s.available(); if (c != null) { input = c.readString(); input = input.substring(0, input.indexOf("\n")); // Only up to the newline data = int(split(input, ' ')); // Split values into an array // Draw line using received coords stroke(0); line(data[0], data[1], data[2], data[3]); } } |
/** * Shared Drawing Canvas (Client) * by Alexander R. Galloway. * * The Processing Client class is instantiated by specifying a remote * address and port number to which the socket connection should be made. * Once the connection is made, the client may read (or write) data to the server. * Before running this program, start the Shared Drawing Canvas (Server) program. */ import processing.net.*; Client c; String input; int data[]; void setup() { size(450, 255); background(204); stroke(0); smooth(); frameRate(10); // Slow it down a little // Connect to the server's IP address and port c = new Client(this, "127.0.0.1", 10002); // Replace with your server's IP and port } void draw() { if (mousePressed == true) { // Draw our line stroke(255); line(pmouseX, pmouseY, mouseX, mouseY); // Send mouse coords to other person c.write(pmouseX + " " + pmouseY + " " + mouseX + " " + mouseY + "\n"); print(pmouseX + " " + pmouseY + " " + mouseX + " " + mouseY + "\n"); } // Receive data from server if (c.available() > 0) { input = c.readString(); input = input.substring(0, input.indexOf("\n")); // Only up to the newline data = int(split(input, ' ')); // Split values into an array // Draw line using received coords stroke(0); line(data[0], data[1], data[2], data[3]); } } |