COPYRIGHT NOTICE. COPYRIGHT 2007-2011 by Clinton Jeffery.
For use only by the University of Idaho CS 428/528 class.
Lecture Notes for CS 428/528 Games and Virtual Environments
Reading Assignment
- Read Chapter 1.1 of the Rabin book, "A Brief History of Video Games"
What's This Course About?
- An introduction to games, genres, game design
- How to design and implement computer-based games
- How to develop collaborative virtual environments
- Graphics, networking, A/I, data structures, algorithms, I/O...
Big questions:
- How can CS reduce the effort required to develop computer games?
- What CS skills can we learn by studying game design and development?
What's This Course NOT About?
- Vocational/technical training for the game industry
- DirectX, Direct3D, or other platform-specific API's
- Modding or "level editing" of other people's games
Pirates
This semester, our theme will be "pirates" and we will write games about
pirates. Dr. Ian Chambers of the UI history department has challenged us
to come up with some games that may be useful in his course, History 421,
Pirates of the Caribbean and Beyond. We may write one or more explicitly
educational games, but pirates are also a popular and marketable subject for
ordinary games designed for pure entertainment.
What is a Game
This course is about computer-based games, so what is a game?
Can you define what a game is? Many definitions will miss the mark
or fall well short. Dictionary.com lists 24 definitions.
The main definitions we will consider are
- an amusement or pastime
- OK, but are movies and TV games? No.
- a competitive activity involving skill, chance, or endurance...
- having rules, etc.
The first definition is missing the interactivity or mental or physical
exercise implied in the second, but it captures the important aspect of
fun. The second definition misses the "fun" and misses the
potential of cooperative games versus traditional competitive games.
Throwing all this together one gets something like:
A competitive or cooperative activity involving skill, chance, or
endurance, whose primary goals are amusement, improvement, or both.
Dr. J's Quick Take on the History of Computer Games
History is not the subject of this course, but here are a few brief landmarks.
How many of the pioneering games do you know? This material is generally
collected from appropriate googling off the web, supplemented occasionally
from the author's memory. Some other good takes on videogame history can
be enjoyed at
Ployojump's site.
1952
| First computer game (?) might have been Alexander Douglas'
computer version of Tic-Tac-Toe (Noughts and Crosses).
Although this is a graphics display, it might as well be ASCII-art
|
1958-59
|
Higinbotham builds
Tennis for Two
This is running on an analog computer.
The screen is an oscilloscope.
Hardware implementation.
One instance built for public demonstration.
This isn't Pong, but it seems valid to call it a predecessor.
Proof that Sports Games came before Shooters.
|
1961-62
|
Russel builds
Spacewar
Software widely replicated as a "diagnostic tool".
Inspired videogame entrepreneurs.
Cloned into commercial existence in 1970's.
|
1960's |
While spacewar was slowly propagating...
games such as checkers considered by A/I pioneers.
Punched card era also inspired trivial sim games (e.g. Hamurabi)
|
70's
|
pencil RPG's
Textual games and ASCII art.
Usually turn-based. On-line == modems: rare and institutional.
The precursor to the FPS: Mazewar
|
late 70's |
Arcade games. Pong, space invaders, Pac Man...
Precursor 4k-64k personal computers often had lower fidelity graphics
than the commercial arcades. Check out C64s.com
|
80's | early multi-user games. (low-res) turn based strategy
Let us not forget awesome console franchise empires such as Nintendo and Sega.
The Rabin book has sections on some of them. You may be nostalgically attached
to one or more iconic figures such as Mario, or Sonic the Hedgehog.
I am not sure that the Rabin book emphasizes
enough the closed and controlled nature of console development, and what
implications it has for the industry.
|
90's | FPS, RTS.
From id software came the game Doom, which catapulted PC gaming to the fore,
and spurred intense development of low-cost mass market 3D graphics hardware.
|
2K+'s | MMRPG, ...
|
Current Platforms
- Wii, Xbox360, PS3
- PC and Mac
- handheld and cellphone
- web
Customer expectations are approximately commensurate with the hardware
capabilities of each platform.
Game Development via "Evolution"
Games are large and complex. This course can be considered a
"specialized, media-rich software engineering class". You can
try to build a game via the waterfall model:
Requirements Analysis
|
- The gamer has an idea, not found in current games
- Idea is explored, and a "vision" for a new game is realized.
|
Design
|
- Game User Interface. File formats. Network communication protocols.
|
Implementation
|
- Coding and Art. Ratio varies. Art is overwhelming coding in modern
commercial games.
|
Testing
|
- Hire gamers at $6/hour to test your game. Alpha and beta test.
|
Release
|
- Release your game through a publisher and distributor, or via internet.
|
Maintenance
|
- If you get this far, you are more successful than 98% of game efforts.
|
Actually, the waterfall model is "all wrong" for game development, which
should probably follow a "natural evolution", meaning working prototypes
start out with textual depictions of core game logic, proceed to simple
graphics, and then evolve more powerful 3D and multi-user capabilities.
Whether or not each individual game should evolve in this manner, there
is another possible application of the metaphor.
Corollary Evolutionary Hypothesis: just as embryos, fetuses, and infants
are claimed to go through the earlier stages of evolution of our species,
our individual capability to write computer games might best be "grown"
one game at a time by implementing each and every game and game genre
that was previously done by our forefathers. When we can clone Wow,
then we can talk about implementing Wow's successor.
Popular Game Genres and Mechanics
Although many games span multiple genres
- parlor games
- computer versions of manual games can be very popular.
- text adventures
- textual games that require players to solve puzzles and mysteries
- arcade
- 2D games of exploration or survival
- first person shooters
- 3D real-time games of combat/survival
How do you know when you win?
Popular game mechanics include one or more of the following:
- survival
- run, or shoot, until you die. Examples: Space Invaders, Doom
- escape
- win by getting out
- genocide
- win by eliminating the opponent. Example: checkers
- assassination
- win by killing the enemy king. Example: chess, stratego.
- collection
- win by completing your collection of rare and exotic items (or deeds)
- construction
- build cool stuff
- tycoon
- win by amassing the most money
- celebrity
- win by amassing the most "fame"
- megalomania
- win by taking over the world, a la Napoleon / Alexander / supervillain
- tactics
- win a battle, by accomplishing specific military objectives
- strategy
- win a war, by defeating the enemy across multiple battles. Usually
involves out-resourcing, out-logisticsing, or out-sciencing them.
Examples: Civilization, Warcraft
- exploration
- win by finding or discovering something, or everything
- role-playing
- you don't win, you just enhance your character, get "xp" and gain "levels"
Learn Unicon by Means of Simple Classic Games (UG Chapters 1-6)
The Icon programming language was invented at the University of Arizona
towards the end of the 1970's. It descended from Snobol, the ultimate
string and text processing language.
procedure main()
write("Hello, amigo!")
end
Roll a dice:
write("die roll: ", ?6)
Flip a coin:
write("coin toss: ", ?["heads", "tails"])
Iterate (form 1: sub-for-loop):
every 1 to 3 do write("crazy")
Store a list of words, and pick a random one.
words := ["fish", "beagle", "minotaur", "tiger", "baseball"]
word := ?words
Scramble the word:
scramble := word
every 1 to 99 do
scramble[?*scramble] :=: scramble[?*scramble]
Whole program:
procedure main()
words := ["fish", "beagle", "minotaur", "tiger", "baseball"]
word := ?words
scramble := word
every 1 to 99 do
scramble[?*scramble] :=: scramble[?*scramble]
write("Unscramble: ", scramble)
answer := read()
if answer == word then
write("correct!")
else write("no, it was ", word)
end
Lecture 2
Reading Assignment
Game Competitions -- for what they're worth
All CS students like games, right? So there are hordes of student programming
competitions with games as their theme. Here are two. The reason I don't
officially endorse any of these is given after the hyphen.
Rabin Book, Chapter 1.2
See highlights from Games and Society
Homework Assignment
See Homework #2
Highlights of
UG book
Chapters 1-6, cont'd
Reminder: The coding here is EASY, but you are supposed to
supply corrections and ask nitpicky questions until you are
very familiar with the syntax of our "very high level
rapid game development language".
Ascii Art:
hangedperson := [
" o ",
"\\ | /",
" \\|/ ",
" /|\\ ",
"/ | \\"
]
Stopping a program with an error message:
if misses = 5 then
stop("you lose! The word was ", word)
Checking if the guessed letter appears, and updating
the string that keeps track of the correct part.
if find(letter, word) then
every i := find(letter, word) do
blanks[i] := word[i]
else misses := misses + 1
Open a window. Draw Stuff:
w := open("hangman", "g", "size=400,400")
DrawCircle(w, 300, 50, 25)
DrawLine(w, 300,75, 300,150)
Chapter 3: Dice Games
Roll 5 dice, Yahtzee style:
dice := [ ?6, ?6, ?6, ?6, ?6 ]
write("The dice are: ",
dice[1], dice[2], dice[3], dice[4], dice[5])
Keeping some dice, re-rolling others:
write("Which dice values do you keep?")
keep := read()
# Reroll the dice
every i := 1 to 5 do {
if not find(dice[i], keep) then
dice[i] := ?6
else keep[find(dice[i], keep)] := ""
}
Sorting the dice:
dice := sort(dice)
Scoring: upper half
if dice[1]=dice[2]=dice[3]=dice[4]=dice[5] then ...
else if dice[1]=dice[2]=dice[3]=dice[4] then ...
else if dice[1]=dice[2]=dice[3] then ...
Brief Summary of Rules of Yahtzee
See
http://grail.sourceforge.net/demo/yahtzee/rules.html
More generally: I welcome input where the Game Book needs additional material,
and when it is missing something, I welcome questions. But when you need some
info like this, please use google and dig it up.
Lecture 3
Mailbag
- How do I declare "private" variables in Unicon classes?
- Sorry, Unicon and many other dynamic languages use "bedroom door privacy",
not "10-foot-walls-between-neighbor's-houses" privacy.
- If someone enters "1 3 5 4 2" how do I get those into separate variables?
- How about
L := []
"1 3 5 4 2" ? {
while put(L, tab(many(&digits))) do
tab(upto(&digits))
}
Rabin of the Day
Let's see if there are any interesting comments in
Chapter 1.3 on Ludology.
Text Adventures and ASCII Art Games
The theme of Chapters 1-6 of the Unicon games book was:
we can write computer programs which reasonably
faithfully implement popular non-computer parlor games. How pretty we make
them is subject
to how much graphics or sound we invest, but the underlying computation
might be complete and identical to the actions of the original game. For
example, using a computer to play checkers with another human is really a
game of checkers, even if no physical board or pieces are involved.
Most computer games are simulations of some imaginative
situation. The computer both (a) allows more detailed simulations than
players can manage using pencil and paper, and (b) frees the player from the
need for a referee or game master.
Text Adventures
See the Chapter 7 description of text adventure gaming and the
show-and-tell book examples of interactive fiction.
The ORIGINAL text adventure
Infocom games (Java applets)
Hamlet adventure
(original cia.icn)
improved cia2.icn
Steve Jackson wrote an
article on writing interactive fictions which may be of interest.
Lecture 4
- Lecture notes from last week
- Thank you, Lawrence! Note to future scribes: No need to repeat
material explicitly given on my lecture notes, but fine to repeat
section headings in order to provide context for your additions.
- Scribe This week:
- Daniel Norris.
Comments on Homeworks
- I expect sophistication of solutions to vary widely
- format your code for printable hard copy
- break long lines; maximum line length is 80
- indent 3-4 spaces per syntax level
- Bugs to Avoid
- no paper copy; paper copy with unreadably small font
- turning in something I can't run; particularly:
- turning in with syntax error
- not writing enough output for user to tell where they are and
what they are doing, user getting "lost"
- defining/applying the rules carefully
- not checking input carefully
- making it unclear how to quit; quitting awkwardly
- if answer == 'y' (no character literals in this language)
- not using +:=, -:=
- not using case str of { "this": { ... } "that": { ... } }
- not using x < y < z
- if find(2&3&4&5, !dice) ...
- using $include when you should link
Reading Assignments
Text Adventures, cont'd
Although text adventures were originally non-graphical, fundamental concepts
here carry over into graphical RPG's and MMO's.
Anatomy of a Text Adventure
A text adventure needs to store a lot of text, either in external file(s)
or directly in the code. The cia program embeds the text in the code.
What the text adventure is really trying to do is not just store text,
it is trying to model an imaginary world and the player's
place, condition, and progress in that world.
Now, let's generalize: what if the goal were a text
adventure program that let you build the text adventure, from inside
the text adventure? Certainly for graphical virtual environments (MMO's,
etc.) it is desirable to allow the world to be built and edited in real
time. A text adventure one logical place to start that process.
There is a source code listing on the class website
for a fragment of such a text adventure builder. Last time I taught
games and virtual environments, students were asked to build out a
fully-functional text adventure builder as their next homework! This
time around I may add the text adventure builder to Chapter 7 of the
Unicon Games Book, but I am not likely to make you write one. On the
other hand, they are useful to study, and it might be good to design
the content for a text adventure version of Treasure Island.
Googling "Treasure Island" and "text adventure" together it is clear
that a lot of pirate-themed text adventures have been done before,
including the 2nd title from Scott Adams, and apparently, a commercial
game for the Commodore 64 that was actually based on Stevenson's novel.
Besides basic room design, and narrative story line (text), one of the
main design issues is regarding state: how much must the system remember
about the actions of the user in order to complete the adventure? Are
all pieces of state simple booleans?
Text Adventure as Finite Automaton
The "world" is a graph (nodes, edges) in which each node is a room or
other discrete location that the player can visit. If each node is
labeled with an integer, the player's location can be stored and
updated as an integer. It might be just as easy to store it as a
string name of the location, such as "kitchen" and that node labeling
may be far easier for humans to understand.
class Room(name, descrip, detail, edges)
# ... methods
initially(nm, desc, det)
name := \nm | "unknown room"
descrip := \desc | "You are in a nondescript empty room."
detail := \det | "There is nothing here."
end
The finite automaton will be represented via a global variable named "rooms"
that holds a table (Unicon's associative array, a.k.a. hash table, dictionary,
etc.). The current state will be represented via a global variable named
"current".
global rooms, current
The main() procedure has to initialize, and then go into the finite
automaton's routine.
procedure main()
rooms := table()
write("Welcome to the text adventure builder")
current := rooms["void"] :=
Room("void","You are in a dark void.","You can't see anything much.")
The machine itself is a fetch-decode-execute loop:
repeat {
# ... tell the situation (where the user is, etc.)
# ... tell what the obvious actions/choices are.
if not ( command := read() ) then stop("Goodbye.")
argv := cmdlist(command)
case argv[1] of {
# ... interpret user actions
}
}
end
Telling the situation and the obvious choices:
write(current.descrip)
every write((!\(current.edges)).descrip)
There is some inevitable "easy" text and list manipulation in
handling this kind of user input...
procedure cmdlist(s)
L := [ ]
trim(s,0) ? {
while put(L, tab(upto(' \t'))) do {
while L[-1][1]=="\"" & L[-1][-1]~=="\"" do {
L[-1] ||:= tab(many(' \t'))
L[-1] ||:= tab(upto(' \t'))
}
if L[-1][1]=="\"" & L[-1][-1]~=="\"" then
L[-1] := L[-1][2:-1]
tab(many(' \t'))
}
put(L, tab(0))
}
return L
end
The interpretation of user actions will vary. Some actions
change the finite automaton's state ("go south"). Some
change the finite automaton itself.
Sim Games
Sim == Simulation. It denotes a computer-based simulation based upon
a simplified/abstracted model of some interesting real-world domain.
Simulation has always been a big application of computing.
There is an obvious relationship/similarity between some turn-based
strategy games and some of these sim games.
Notes on Hamurabi
Chapter 8 of the Unicon Games Book presents the first and simplest
"sim" game, Hamurabi. You are the king of an ancient mesopotamian
city-state. Let's take a look at the gameplay.
The main procedure simply creates one city instance and calls its play()
method.
procedure main()
Babylon().play()
end
Hamurabi relies on a few variables and updates them each turn based
on a combination of user choice and random fluctuation (weather, etc).
class Babylon (
Z, # Year
D, # People died this turn
D1, # People died, cumulative
I, # Immigrants
P, # Population
P1, # Starvation percentage
A, # Acres
Y, # Yield
E, # Eaten
S, # Store
C, # Random yearly yield modifier
Q, # Input
L, # Acres per person
H
)
Each turn follows a simple 5-step pattern to convey one simulation-year's
activities.
method play()
repeat {
report()
buy()
sell()
plant()
harvest()
}
end
We won't cover the whole application in class, but a typical game turn phase
involves some random numbers and some user queries to determine an outcome.
Note the classic (i.e. poor) BASIC variable names, and the use of a loop to
force the user to eventually enter an allowable figure.
method buy()
Y := ?11 + 16
write("Land is trading at " , Y , " bushels per acre.")
repeat {
writes("How many acres do you wish to buy? ")
if not (Q := integer(read())) then {
write("Try using the newfangled Arabic digits, please.")
next
}
if Q < 0 then finish("quit")
else if Y * Q <= S then return
else {
write("Hamurabi: think again. You have only ", S)
write(" bushels of grain. Now then,")
}
}
end
Thoughts on Taipan
Another text-based simulation game everyone should be familiar with
is Taipan. Compared with
Hamurabi, it simulates multiple cities and multiple commodities
(not their agricultural production, but the prices that result),
and forms the
basis for several pirate-games' focus on accumulating of wealth
by seizing other ships' regular non-treasure cargo, sailing to a
port, and selling it (used in Sid Meier's Pirates, Port Royale, ...).
Lecture 5
Blurbs
- Mailbag Q:
Is there a self reference available in an object, as is
available in a C++ object?
-
A: Yes, its name is
self
and it is seldom used.
self.x
is the same as x
unless you
had a local or parameter named x;
self.m()
is the same as m()
unless you had a local
or parameter named m. You can pass self
as a parameter, insert
it into lists and tables,
etc. About the only place where self
is usually required though
is when you want to call a
superclass method that has been overridden, as in
self.superclassname.m()
- Tips from an Office Hour Session
- Windows Unicon doesn't like being installed in a path with spaces,
such as \Program Files. It and a couple other bugs will likely be
addressed in a new binary build sometime next week. :-)
- CS 428/528 Forum
- Topic "MMO Experiences" was added for those of you who plan to waive
your HW#2 Part Two (which mainly means: try out Lord of the Rings Online
with the class).
- Achaea
- Last class there was reference to this "text-based MMO".
Of course, the term for "text-based MMO" is MUD, standing for
Multi-User Dungeon or Multi-User Domain. MOO is also a synonym,
more or less. Achaea appears to be web-based and slick, but
I failed to create a character on my first try. Still it may
be worth your checking out.
- Battle for Wesnoth
- I can already tell from a single screenshot that I am going to love
Battle for Wesnoth...because it uses hex maps that remind me of some of
my favorite old table-top wargames. Turn-based 2D graphics, a logical
intermediate step between ASCII art games and 2D real-time games.
- From Bruce Bolden
-
Bruce sent me a link to a Gamasutra article about Magicka's day #1 sales. This one was started as a student
project. The point? With the right game idea and mad development skills,
any one of you could make this happen here.
Taipan, cont'd
Let's finish our discussion/demo of Taipan. What you are really supposed
to understand from this classic game is the accumulation of wealth by
buying commodities at a low price and selling them at a high one. The
ship battle mechanics in Taipan are truly laughable.
Thought exercise: what would it look like to merge Taipan's mechanics
with Hamurabi's? Taipan fluctuates commodity prices at the cities
randomly each turn. What would it take to tie commodity prices more
intelligently to different forms of government, or different governing
AI personalities (wise, lazy, warlike...)?
Note that Rabin's 2nd edition re-organized relative to the 1st edition and
first edition slides; it has also conflicting titles for Chapter 2.1
(Games and Society vs. Game Design) discussed last time.
There are like 108 slides for Rabin 1ed chapter on game design.
Some are weak, but we'll review the batch for useful ideas over 1 or more
class periods. Today we covered about through slide 48 or so.
From Text to 2D
Real life is continuous, while computers are discrete and offer only
approximations of continuity. Text adventures typically offer
only a small number -- say, 10 or 20 -- of possible
locations. The user's movements are typically tracked at room
granularity.
Even with a lowly 80x24 text-only terminal, a 2D application can
approximate with better precision and detail than this, as seen in the
classic game rogue, or this screenshot of nethack:
80x24 = 1920 possible positions per screen. As you can see from this
nethack screenshot, each screen/level of rogue or nethack might provide
5-20 rooms, with more detailed navigation than a text adventure utilizes.
Rogue had on the order of 17-25 such levels; nethack has far, far more.
One thing I miss in nethack compared with text adventures is the detailed
text description of the rooms; there is no real reason in a game like
Pirate Duel why the current room
or corridor you are in could not have a supporting text description.
Note the similarities between this and the new chapter 9 for the Unicon
games book "Pirate Duel".
See piraduel.icn and
ship.dat.
Besides the "rogue-style" transition to text-mode as a low-resolution 2D
graphics display, another intermediate point between the text mode games
we have seen so far and the animated games that we will be discussing
shortly are the early graphical adventures such as
"The Secret of Monkey Island".
A graphical adventure of this sort simply substitutes a 2D image for the
basic textual description of the current room. Note the easier-than-pie
point-and-click menu of all the verbs that are allowed at any given point!
Comments on Homeworks
- First priority is of course correctness, then "look and feel"
- Beyond these, your goal is to create
the most "elegant" solution. "Elegant" mostly means shortest but
most readable. Try writing your program as poetry, even a
limerick or haiku, instead
of a novel. This affects maintainability and extensibility.
Arcade games
Unicon Games Book Chapters 10-12 present a series of (clones of)
classic 2D arcade games (Pong, Breakout, Tetris, Asteroids...).
Basic principles of such games include:
- smooth animation
- highly responsive user input
- gameplay highly repetitive
- based on speed and coordination
This course isn't really about computer graphics programming per se.
Ping here is not the famous internet utility, but the clone of "Pong".
--> What is the fatal flaw in this immaculate code?
Initialization:
procedure main()
&window := open("Ping","g", "size=640,480",
"bg=black", "fg=white")
FillRectangle(10,220,10,40)
FillRectangle(318,0,4,480)
FillRectangle(620,220,10,40)
FillRectangle(340,240,10,10) # ball
x := 340
y := 240
dx := 1
dy := 0
p1x := 10
p2x := 620
p1y := p2y := 220
p1score := p2score := 0
Main Loop / Ball Movement
repeat {
# erase ball at old position
EraseArea(x,y,10,10)
FillRectangle(318,0,4,480)
# calculate new position
x +:= dx
y +:= dy
# draw ball at new position
FillRectangle(x,y,10,10)
WSync()
"Physics" :-)
# bounce off paddles
if p1x <= x <= p1x + 10 &
p1y <= y+10 <= p1y + 40 then {
dx := dx * -1
dy +:= ((y+5) - (p1y+20))/15
}
if p2x <= x+10 <= p2x + 10 &
p2y <= y+10 <= p2y + 40 then {
dx := dx * -1
dy +:= ((y+5) - (p2y+20))/15
}
# bounce off top or bottom wall
if y <= 0 then dy := dy * -1
if y+10 >= 480 then dy := dy * -1
Score points when the ball hits
the left or right edge
if x + 10 > 640 then {
dx := dx * -1
p1score +:= 1
dy := 0
GotoXY(10,10)
writes(&window, p1score)
}
if x <= 0 then {
dx := dx * -1
p2score +:= 1
dy := 0
GotoXY(600,10)
writes(&window, p2score)
}
Timing
delay(8)
Paddle Control / User Input
# move paddles up or down in response to user input
if (* Pending() > 0) then {
e := Event()
if e == ("q"|"a") then {
EraseArea(p1x,p1y,10,40)
if e == "q" then p1y -:= 5
else p1y +:= 5
FillRectangle(p1x,p1y,10,40)
}
else if e == ("p"|"l") then {
EraseArea(p2x,p2y,10,40)
if e == "p" then p2y -:= 5
else p2y +:= 5
FillRectangle(p2x,p2y,10,40)
}
}
}
Event()
end
And the fatal flaw is...? Paddle movement rate is governed by the rate at
which either (a) the user pounds the keys repeatedly, or (b) the OS
and/or Window System supplies those keys via "keyboard repeat".
Arcade Games typically require lower-level input handling. Minimally,
asking the operating system for separate "key press" and "key release"
events. In unicon this is requested by the attribute assignment
"inputmask=k". Key release events are negative integers (-ord(c)-128
for key c, where ord(c) is the ASCII code for character c).
Exercise: modify ping.icn to keep moving upwards as long as
the paddle's "up" key is pressed, and stop when it is released.
lecture 6
Announcements
- ECE Research Colloquium, Thursday 3:30 TLC044
- "What can we learn about our bodies and minds by researching robotics?"
- Microsoft Imagine Cup thingie, next Tuesday 4:30 JEB 328
- free pizza. and hey, its Microsoft, so it must be good
Supplemental Lecture Notes
- Week 2 Notes are here
- This week: Gary Hollingshead
Head Check: How Many Have Working LOTRO Accounts ?
- Are we ready to have our official class MMO educational session yet?
- The server on which I can render the most assistance, and therefore
recommend for class use, is named "Imladris".
- create a character there, and try to play it to level 10 or so
- meet at the "Prancing Pony" inn, in Bree.
- date and time for an in-game session: (tbd)
- We will cover the 2nd half of the Rabin 2.2 slides.
Tony Downey supplement
- Since we are talking about game design, you may also find
this
Tony Downey blog article interesting. Please read it. Thank Bruce.
Key subpoints to consider (and agree or disagree with):
- Prototype
- Design is usually by trial and error, not by flashes of brilliance
- Gameplay trumps content
- Dynamic content trumps static content
- Progress, Obstacles, Resources - the big three mechanics
- Avoid perfectionism and NIH syndrome - you don't have to invent it all
- [Jeffery] ... but canned game frameworks can overly constrain
your game.
- "variable driven development" - identify explicit simulation parameters.
use variables, not constants. consider a runtime interface for
tweaking them.
- community -- high scores, in-game expressions of identity, status
- power law -- free to play, sell add-ons later
- collect and analyze player data
- tutorial ? I thought Minecraft was lauded for not having a tutorial...
- demo - learn by watching
- guidepost - learn by in-game signs
- sandbox - learn in a danger-proof environment
- sensei - learn from a recurring relationship with a master
A Word on Unicon Objects
Per a student request, here is an action summary recap of Unicon classes and
objects. The material is from "Programming with Unicon" chapters 8-12 or so.
In Unicon...
- class syntax was derived from record declaration syntax.
- record foo(x,y) became
class foo(x,y)
method m(z)
...
end
end
- inheritance is class foo : bar : baz (x,y) ...
- superclasses bar and baz are looted, in that order, for stuff to
add to foo. If its already "got one" you don't add that field/method.
It is called "closure-based inheritance" and it is quite intuitive.
- Class constructors are like record constructors: v := foo(1,2)
- except that they execute an initially section if the class has one.
If the initially section has a parameter list it overrides the default
record-style binding of parameters to class fields.
- No destructors
- Garbage collection. You can by convention define your own explicit
destructor protocol when needed. For example GUI widgets do so.
- Method invocation is o.m(1,2,3)
- except rarely when you use o$superclass.m(1,2,3)
- Objects play nicely with Icon's structure types
- Typical is objects with lists or tables of other objects...
Brick
Brick is a clone of the Breakout game, an early successor for Pong. In its
basic form it is one player against the computer.
--> What is the near-fatal flaw in this elegant code?
The class Brick manages an individual block. Each brick remembers its location
and color. It knows how to score itself,
how to draw and erase itself, and how to tell whether a point is inside it.
Note that everything is an expression, including "case" (switch) expressions.
Note that cases can switch on arbitrary values, not just integers.
class Brick(x, y, color)
method score()
return case color of {
"blue": 10
"yellow orange" : 5
"green": 1
}
end
method draw()
Fg(color)
FillRectangle(x,y,45,20)
end
method erase()
color := "black"
EraseArea(x,y,45,20)
end
method hittest(ballx, bally)
if color == "black" then fail
if x-10 < ballx < x + 45 &
y-10 < bally < y + 20 then return
fail
end
end
The main procedure for breakout
procedure main()
bricks := [ ]
&window := open("Bricks", "g",
"size=640,480",
"fg=white","bg=black")
row := 0
every row := 0 to 7 do {
if row = 0 then
color := "blue"
else if 1 <= row <= 4 then
color := "yellow orange"
else if row > 4 then color := "green"
put(bricks, [ ] )
every column := 0 to 12 do {
b := Brick(column * 49 + 2,
row * 24 + 12, color)
put(bricks[ -1 ], b)
}
}
every (!!bricks).draw()
Fg("white")
FillRectangle(320,460,40,10)
FillRectangle(320,260,10,10) # ball
x := 320
y := 260
dx := 0
dy := 1
px := 320
py := 460
score := 0
lives := 0
repeat {
# erase ball at old position
EraseArea(x,y,10,10)
# calculate new position
x +:= dx
y +:= dy
# draw ball at new position
FillRectangle(x,y,10,10)
WSync()
# bounce off paddle
if px <= x <= px + 40 &
py <= y+10 <= py + 10 then {
dy := dy * -1
dx +:= ((x+5) - (px+20))/10
}
# bounce off left, right, or top wall
if x <= 0 then dx := dx * -1
if x+10 >= 640 then dx := dx * -1
if y <= 0 then dy := dy * -1
# take a life when the ball hits the bottom edge
if y + 10 > 480 then {
dy := dy * -1
lives +:= 1
dx := 0
if lives = 4 then {
WAttrib("label=Bricks [game over, q to quit] _
score: " || score)
while *Pending() > 0 do Event()
while Event() ~== "q"
exit(0)
}
else
WAttrib("label=Bricks [" || lives || " / " ||
"4] score: " || score)
}
every b := !!bricks do {
if b.hittest(x,y) then {
score +:= b.score()
b.erase()
WAttrib("label=Bricks [" || lives ||
" / " || "4] score: "|| score)
dy *:= -1
}
}
# delay(1)
every 1 to 75000
# move paddle left or right in response to the user
EraseArea(px,py,40,10)
px := QueryPointer()
FillRectangle(px,py,40,10)
}
Event()
end
Lecture 7.
Preliminary Comments on HW#2
- Submitted solutions by 20 students appear to be all over the map
in terms of language, IDE, OS, compiler, library requirements.
C++/Qt, Ruby/Gosu, ObjC/Cocoa, Virtools, etc. etc.
- Requirement is that I be able to compile and run your code.
- Burden of proof that you meet this requirement is on the student.
- As of 1pm, one student HW2 has met the burden of proof (Miller, on Apple)
- Mr. Norris, your instructions were good, thank you, I will have a go at
it.
So far, some other students have submitted assignments without adequate
instructions, without makefiles, and without hope they will pass the
burden of proof. :-)
- I am willing to legally install compilers/IDEs/libraries in order to
grade your work. If I have to.
- I am not willing to pay for software in order to grade your work, nor
(probably) to install a new OS in order to grade your work.
Quick Look at HW#3
Why is the Game of Life (a math toy) interesting for game developers?
- Simple rules can generate complex behaviors
- May provide suggestions or building blocks for
self-balancing dynamic virtual worlds.
Cellular automata are sooooo cute. How do we get from them to, say,
a civilization of Dwarves?
Step 1 might be to have them fight each other, as marketed by asterisksoft.
There are similar, elaborated versions involving sharks and fishes.
Life Source Code
This code is adapted from Steve Wampler's solution on RosettaCode.org.
global limit # optional limit to number of generations
procedure main(args)
n := args[1] | 25 # default is a 25x25 grid
limit := args[2] | &null
&window := open("life - Enter the starting pattern, end with EOF",
"g","size=450,450","font=typewriter,13")
grid := getInitialGrid(n)
play(grid)
end
# This procedure reads in the initial pattern, inserting it
# into an nXn grid of cells. The nXn grid also gets a
# new border of empty cells, which just makes the test simpler
# for determining what do with a cell on each generation.
# It would be better to let the user move the cursor and click
# on cells to create/delete living cells, but this version
# assumes a simple ASCII terminal.
procedure getInitialGrid(n)
static notBlank, allStars
initial {
notBlank := ~' '
allStars := repl("*",*notBlank)
}
g := list(n+2, repl(" ",n+2)) # store as an array of strings
while (e := Event()) ~=== "q" do {
if e === &lpress then {
g[&row,&col] := toggle(g[&row,&col])
}
if e === &ldrag then {
g[&row,&col] := "*"
}
display(g)
}
return g
end
procedure toggle(s)
if s === " " then return "*" else return " "
end
# Simple-minded procedure to 'play' Life from a starting grid.
procedure play(grid)
while not allDone(grid) do {
display(grid)
if Event() === "q" then break
grid := onePlay(grid)
}
end
# Display the grid
procedure display(g)
EraseArea()
GotoRC(1,1)
every write(&window, !g)
end
# Compute one generation of Life from the current one.
procedure onePlay(g)
ng := copy(g)
every ng[r := 2 to *g-1][c := 2 to *g-1] := case sum(g,r,c) of {
3: "*" # cell lives (or is born)
2: g[r][c] # cell unchanged
default: " " # cell dead
}
return ng
end
# Return the number of living cells surrounding the current cell.
procedure sum(g,r,c)
cnt := 0
every (i := -1 to 1, j := -1 to 1) do
if ((i ~= 0) | (j ~= 0)) & (g[r+i][c+j] == "*") then cnt +:= 1
return cnt
end
# Check to see if all the cells have died or we've exceeded the
# number of allowed generations.
procedure allDone(g)
static count
initial count := 0
return ((count +:= 1) > \limit) | (trim(!g) == " ")
end
Rabin
Chapter 3.1, Teams and Processes
only 26 slides, includes lots of CS383 material we will not dwell on
Arcade Game Framework
Can all Arcade Games follow the following formula?
# open window
repeat {
# erase moving objects from their old positions
# draw moving objects at their new positions
# read user input
# calculate all deaths, births, and changes in movement
}
What's the difference (in this framework)
between turn-based games and real-time games?
Lecture 8
Career Fair
The "All Majors" Career Fair is tomorrow Wednesday Feb 9 on the Moscow
campus
and features around a half-dozen employers specifically recruiting for
CS summer, intern, entry level or senior positions.
Supplemental Lecture Notes
- Week 3 Notes are here
- This week: Joel Kim
- Taking a quick peek at the schedule, if I missed your sign-up, please remind me when you would like to take notes.
HW#2 Comments
HW#2 Evaluation Status Update
Arbitrarily I will present them in increasing order of lines of code.
- Cody Miller, on Apple. 1 KLOC of ObjC/Cocoa.
- Cute, nice piratey dialog, no action.
- BCK's Virtools. ?? KLOC. 52MB. violin music. side-scroller-feel.
- Max Stillwell's 1.5 KLOC of C++/Qt.
- Doug Drobny's, 660 LOC of C++/curses
- Jacob Flynn's 330 lines of Unicon
- Nathan Mazur's 800 lines of Unicon
- Four pirates in an extensible pirate npc config data file format.
- Dallas Stinger's 2750 lines of C#
- Ship to ship combat, sort of; boats move/sail a bit.
- Brett Hitchcock's 390 lines of C++ with SDL
- Zach Kimball's 1.9K lines of Python with PyQt
- Jamie Nance's 572 lines of Unicon
- Daniel Norris's 360 lines of Ruby with GOSU
Rabin of the Day
Chapter 3.2, Languages
I am biased about just how neanderthal the games industry
has traditionally been in terms of languages and tools.
Lots of early games were written in BASIC and were slow.
After, DOOM, when speed ruled on non-GPU hardware, the
dinosaur-mentality was somewhat excuseable. I have old books
on "Game Programming" which are really VGA assembler language
trickbooks.
Sesrit
A high school student (David Rice) wrote a nice version of Tetris for
me some time ago, which is described in the games book. Although you
can learn many things from it, I would like to mention only a couple.
I am not going into the whole program here because it is not
object-oriented, does not use a GUI, etc. It should probably be updated.
But it is a good example of using the built-in Icon/Unicon 2D graphics.
...
/&window := WOpen("label=sesrit","size=276,510", "posx=20")
colors := table(&window)
every c := ("blue"|"yellow"|"cyan"|"green"|"red"|"white"|
"red-yellow" | "purple-magenta" | "pink") do
colors[c] := Clone("fg=" || c)
L appears to be a matrix of
How does the following code compare with L := list(30, list(10, "black")) ?
L := list(30)
every !L := list(10, "black")
By the way, if a point were a two element list of coordinates, one could
access the color at that position via L[p[1],p[2]]. Since it is row-major,
if point p were a record one might write L[p.y,p.x].
How does the following game loop compare with the Arcade Framework
that I proposed above?
procedure game_loop()
game_status := 1
repeat {
while *Pending() > 0 do {
case Event() of {
Key_Left : move_piece(-1, 0)
Key_Right : move_piece(1, 0)
Key_Down : rotate_piece(1, -1)
Key_Up : rotate_piece(-1, 1)
" " : while move_piece(0,1) # drop piece to bottom
"q" : if &meta then exit()
"a" : if &meta then about_sesrit()
"p" : if (&meta & game_status = 1) then pause()
"n" : if &meta then return
&lpress : {
if 15 <= &x <= 105 then {
if 270 <= &y <= 290 then pause()
else if 300 <= &y <= 320 then return
else if 360 <= &y <= 380 then
about_sesrit()
}
}
&lrelease :
if ((15 <= &x <= 105) & (330 <= &y <= 350)) then
exit()
}
}
if not move_piece(0,1) then {
if (!activecells)[1] < 2 then {
game_over()
return
}
# if we couldn't drop the piece down, insert a new piece
}
# ... misc. step code
WSync()
delay(delaytime)
}
end
A "piece" on the board is just a list of coordinates, each of which
is just a list of two integers.
...
case nextcolor := ?["red-yellow", "red", "yellow", "green",
"cyan", "blue", "purple-magenta"] of {
"red-yellow": nextpiece := [ [1,5], [1,6], [2,5], [2,6] ]
"yellow": nextpiece := [ [2,6], [1,6], [2,5], [2,7] ]
"blue": nextpiece := [ [2,6], [1,5], [2,5], [2,7] ]
"purple-magenta": nextpiece := [ [2,6], [1,7], [2,5], [2,7] ]
"red": nextpiece := [ [3,6], [1,6], [2,6], [4,6] ]
"green": nextpiece := [ [2,6], [1,5], [1,6], [2,7] ]
"cyan": nextpiece := [ [2,6], [1,6], [1,7], [2,5] ]
}
GUI's
How do GUI's affect the Arcade Game framework?
Most game engines do not peacefully co-exist with
standard GUI class libraries!
- Custom GUI built into engine?
- Tweaks to both sides to play together nicely?
Lecture 9. GUIs; Flash.
Mailbag
- I created my LOTRO character on the wrong server, what do I do?
- According to one website I read, transferring a character to another
server costs $24.95. Presumably it is done through the LOTRO store.
Another webpage I read said that its only allowed if the server
you are transferring to is not overcrowded. Imladris is not overcrowded
but you may prefer to just create a new character.
- I didn't get to participate in last night's session! What do I do?
- Although I will be playing LOTRO all semester and would be happy
to schedule additional sessions by appointment, you are welcome to
get your MMO experience with or without the company of your classmates
or instructor. Please learn enough about MMO's to cover the topics
mentioned in HW-LOTRO.
- How do I get the y value from QueryPointer()?
- QueryPointer() is documented in "Graphics Programming in Icon". It is a
generator. You can say
L := []; every put(L, QueryPointer())
or if you want to be gross:
every y := QueryPointer() do /x := y
Think and ask questions if you don't "get" this one.
Rivendell Report
Last night about 10-12 students met up in the Prancing Pony.
We decided to do a suicide mission, following the path of the
suicide mission described in the LOTR books and movies: head
from Bree to Rivendell.
We headed unevenfully out of Bree on the main road (not avoiding
Black Riders like Frodo had to), past the Forsaken Inn, from which
point we had some fun swarming to kill things that would have
wiped us out individually. We had one really good minstrel (Vil.);
the other minstrel (Godivadoc) was not acting like one.
We got to the last bridge and entered the Trollshaws, and quickly were
wiped out. Level 10 characters in a level 30-something zone attract even
the flies and the deer to swarm and attack. A level 25 healer can neither
heal fast enough nor stay alive, when the party is attacked by several
level 33 monsters at once. After being killed, if you are beneath a
certain level, you restart at home 1000+ miles away, even if you are
"revived" by a healer's so-called resurrection spell.
Eventually, we resorted to an escort by a level 39 hunter and 38 champion,
and even they were not able to keep the whole party alive in the face of
constant swarming attacks by all animals within range. We had to fall
back on the "pirate's code", and press on. In the end, 5 students
(I think) made it to Rivendell, but only after multiple deaths and with
the assistance of a 7th grader staying up past his bedtime. Rivendell
is of course a major resort destinations, with glamorous celebrities, spas,
elven forges and the like. Kudos to those who made it, and apologies to
those who fell behind.
GUIs in Games
- You may want a Graphical User Interface (GUI)
for your arcade or strategy games.
- Key issue: how are GUI components and graphics mixed?
- if you are real, real lucky, your language/engine does it for you
- and doesn't leave you stuck in a limited-option world
- graphics-primitives-are-GUI-widgets model
- pain to write a new widget for every graphical entity in a game
- build custom GUI using graphics primitives
- pain to develop enough GUI from scratch
- independent-subwindow "view" approach
- moderately easy to mashup a Frankeninterface; looks a bit old school.
Compare early EverQuest with later MMOs.
GUIs in Unicon
See UTR 6 and
Chapter 18 of the Unicon Book.
Basic principles of Unicon GUIs. How does your chosen language do the
comparables?
- Dialog
- The root of GUI components, tied to a window.
- Component
- Superclass of all widgets. Contains subwidgets. Tree of Clone()s.
- Listeners and event-driven programming
- Dispatcher, show_modal() vs. show_modeless()
Rabin of the Day
Chapter 3.3, Macromedia Flash.
- I have allowed students to turn in assignments using Flash before.
They were competitive with solutions done by other students.
- I am not a fan of proprietary or semi-proprietary languages like Flash
- I feel similarly about Virtools.
- I am real nervous...
- if the end product won't run on all my platforms
- if I am a slave to vendor lock-in;
- if I can't control whether I will still be able to make changes
and compile my programs in 10 years...
Sesrit was an arcade game with a simple user interface, still done by
hand. Blasteroids is an arcade example with a GUI. It was done by an
undergrad student, Jared Kuhn, a long time ago, and therefore represents
mixed coding style and is somewhat dated. Blaster is about 1500 lines.
bash-3.2$ wc -l *.icn
91 aboutbox.icn
289 blaster.icn
756 game.icn
428 optionsdialog.icn
1564 total
The makefile is simple. Complexity is introduced if you do inheritance
across files, include files, packages, etc.
blaster: blaster.u game.u optionsdialog.u aboutbox.u
unicon blaster.u game.u optionsdialog.u aboutbox.u
blaster.u: blaster.icn
unicon -c blaster
game.u: game.icn
unicon -c game
aboutbox.u: aboutbox.icn
unicon -c aboutbox
optionsdialog.u: optionsdialog.icn
unicon -c optionsdialog
We show the aboutbox, before the main game, as a trivial GUI example.
The options dialog is similar and will not be presented.
- aboutbox.icn is 92 lines.
- Almost all machine-generated by Ivib.
- Actual Ivib data is contained in an enormous multiline comment at end.
- Old application, required migration between the older
Version 1 GUI classes and newer version 2.
- Major difference between V1 and V2 Unicon GUIs:
a "dialog_event" method was replaced by a Listener architecture.
Non-issue for you folks.
import gui
$include "guih.icn"
class dialogabout : Dialog(text_button_1, text_list_1)
method handle_text_button_1(ev)
dispose()
end
method handle_text_list_1(ev)
end
method handle_default(ev)
end
method dialog_event(ev)
case ev.get_component() of {
text_button_1 : handle_text_button_1(ev)
text_list_1 : handle_text_list_1(ev)
default : handle_default(ev)
}
end
method init_dialog()
end
method end_dialog()
end
method handle_v2_events(ev)
dialog_event(V2TOV1(ev))
end
method setup()
self.set_attribs("size=250,150", "bg=pale gray")
text_button_1 := TextButton()
text_button_1.set_pos("122", "123")
text_button_1.set_align("c", "t")
text_button_1.connect(self, "handle_v2_events", &null)
text_button_1.clear_toggles()
text_button_1.set_label("Okay")
text_button_1.set_internal_alignment("c")
self.add(text_button_1)
text_list_1 := TextList()
text_list_1.set_pos("26", "5")
text_list_1.set_size("200", "75%")
text_list_1.set_draw_border()
text_list_1.connect(self, "handle_v2_events", &null)
text_list_1.set_select_many()
text_list_1.set_contents(["Blasteroids ", "by Jared Kuhn and Clint Jeffery", "Version 2.0", ""])
self.add(text_list_1)
self.connect(self, "handle_v2_events", &null)
end
method component_setup()
self.setup()
end
initially
self.Dialog.initially()
end
### Ivib-v2 layout ##
#class|Canvas|17|Name Table|table|integer|0|2|string|text_button|intege
#r|1|string|text_list|integer|1|SuperClass Name|string|Dialog|Import Na
#me|string|gui|Button Groups|class|ButtonGroupSet|2|Parent Canvas|1|Box
#es|list|0|Checkbox Groups|class|CheckBoxGroupSet|2|Parent Canvas|1|Box
#es|list|0|Gen Indent|integer|3|Gen Main|integer|1|Gen Component Setup|
#integer|1|Gen Init Dialog|integer|1|Gen Initially|integer|1|Dialog Str
#uct|class|CDialog|4|Min Width|null|Min Height|null|Ticker Rate|null|At
#tribs|list|1|string|bg=pale gray|Name|string|dialogabout|Width|integer
#|250|Height|integer|150|Items|list|2|class|CanvasTextButton|36|Parent
#Canvas|1|Name|string|text_button_1|Class Name|string|TextButton|Import
# Name|string|gui|X Fix|null|Y Fix|null|W Fix|null|H Fix|null|W Default
#|integer|1|H Default|integer|1|X Spec|string|122|Y Spec|string|123|W S
#pec|integer|48|H Spec|integer|23|X Align|string|c|Y Align|string|t|Is
#shaded|null|Is Button Subclass|integer|1|Draw Border|null|Attribs|list
#|0|Tooltip|null|Accel|null|Event Handlers|list|1|list|2|string|&null|s
#tring|handle_v2_events|Class Variable|integer|1|Parent Component|1|Lab
#el|string|Okay|No Keyboard|null|Img Up|null|Img Down|null|Img Width|nu
#ll|Img Height|null|Is Checked Flag|null|Is Checkbox Flag|null|Parent C
#heckBoxGroup|null|Parent Button Group|null|Internal Align|string|c|cla
#ss|CanvasTextList|29|Parent Canvas|1|Name|string|text_list_1|Class Nam
#e|string|TextList|Import Name|string|gui|X Fix|null|Y Fix|null|W Fix|n
#ull|H Fix|null|W Default|null|H Default|null|X Spec|string|26|Y Spec|s
#tring|5|W Spec|string|200|H Spec|string|75%|X Align|string|l|Y Align|s
#tring|t|Is shaded|null|Is Button Subclass|null|Draw Border|integer|1|A
#ttribs|list|0|Tooltip|null|Accel|null|Event Handlers|list|1|list|2|str
#ing|&null|string|handle_v2_events|Class Variable|integer|1|Parent Comp
#onent|1|Select One|null|Select Many|integer|1|Checked|list|4|null|null
#|null|null|Contents|list|4|string|Blasteroids |string|by Jared Kuhn an
#d Clint Jeffery|string|Version 2.0|string||Initial Focus|null|Event Ha
#ndlers|list|1|list|2|string|&null|string|handle_v2_events|
Blaster.icn
main() module for blasteroids is fairly short (289 lines).
Another Dialog; a lot of Ivib-generated code. Note "inputmask=k"
and generated main() procedure.
#
# blaster.icn - main module for Kuhn and Jeffery's Blasteroids
#
import gui
$include "guih.icn"
class blaster : Dialog(
text_menu_item_3, text_menu_item_2, text_menu_item_1, text_menu_item_6,
check_box_menu_item_1, check_box_menu_item_2,
text_menu_item_5, text_menu_item_4,
roids, optionsbox, doneoptions)
method handle_text_menu_item_3(ev)
roids.tapped := 2
roids.gamestart := 1
roids.handle_event()
end
method handle_text_menu_item_2(ev)
end
method handle_text_menu_item_1(ev)
dispose()
end
method handle_text_menu_item_6(ev)
if doneoptions = 0 then {
doneoptions := 1
optionsbox.num_ships := 3
optionsbox.num_range := 10
optionsbox.chk_explode := (1=1)
optionsbox.chk_sound := &null
optionsbox.num_torpedos := 3
optionsbox.num_speed := 5
optionsbox.num_rotation := 5
optionsbox.num_level := 1
}
optionsbox.show_modal()
if optionsbox.hitokay = 1 then {
optionsbox.hitokay := 0
roids.num_ships := optionsbox.num_ships
roids.num_range := optionsbox.num_range
roids.chk_explode := optionsbox.chk_explode
roids.chk_sound := optionsbox.chk_sound
roids.num_torpedos := optionsbox.num_torpedos
roids.num_speed := optionsbox.num_speed
roids.num_rotation := optionsbox.num_rotation
roids.num_level := optionsbox.num_level
}
end
method handle_check_box_menu_item_1(ev)
end
method handle_check_box_menu_item_2(ev)
if f := open("highscor") then {
L := [ ]
while put(L, read(f))
close(f)
sort(L)
write("HIGH SCORES:")
every write(!L)
}
end
method handle_text_menu_item_5(ev)
end
method handle_text_menu_item_4(ev)
dialogabout().show_modal()
end
method handle_default(ev)
end
method dialog_event(ev)
case ev.get_component() of {
text_menu_item_3 : handle_text_menu_item_3(ev)
text_menu_item_2 : handle_text_menu_item_2(ev)
text_menu_item_1 : handle_text_menu_item_1(ev)
text_menu_item_6 : handle_text_menu_item_6(ev)
check_box_menu_item_1 : handle_check_box_menu_item_1(ev)
check_box_menu_item_2 : handle_check_box_menu_item_2(ev)
text_menu_item_5 : handle_text_menu_item_5(ev)
text_menu_item_4 : handle_text_menu_item_4(ev)
default : handle_default(ev)
}
end
method init_dialog()
WAttrib(win, "inputmask=k")
set_ticker(1)
end
method end_dialog()
end
method handle_v2_events(ev)
dialog_event(V2TOV1(ev))
end
method tick()
roids.idle_func()
end
method setup()
local menu_1, menu_2, menu_3, menu_bar_1, menu_separator_1
self.set_attribs("size=500,500", "bg=pale gray")
menu_bar_1 := MenuBar()
menu_bar_1.set_pos("0", "0")
menu_1 := Menu()
menu_1.set_label("Game")
text_menu_item_3 := TextMenuItem()
text_menu_item_3.set_label("New")
text_menu_item_3.connect(self, "handle_v2_events", ACTION_EVENT)
menu_1.add(text_menu_item_3)
text_menu_item_2 := TextMenuItem()
text_menu_item_2.set_label("High Scores...")
text_menu_item_2.connect(self, "handle_v2_events", ACTION_EVENT)
menu_1.add(text_menu_item_2)
text_menu_item_1 := TextMenuItem()
text_menu_item_1.set_label("Quit")
text_menu_item_1.connect(self, "handle_v2_events", ACTION_EVENT)
menu_1.add(text_menu_item_1)
menu_bar_1.add(menu_1)
menu_2 := Menu()
menu_2.set_label("Options")
text_menu_item_6 := TextMenuItem()
text_menu_item_6.set_label("Games Options...")
text_menu_item_6.connect(self, "handle_v2_events", ACTION_EVENT)
menu_2.add(text_menu_item_6)
check_box_menu_item_1 := CheckBoxMenuItem()
check_box_menu_item_1.set_label("Sound")
check_box_menu_item_1.connect(self, "handle_v2_events", ACTION_EVENT)
check_box_menu_item_1.set_is_checked()
menu_2.add(check_box_menu_item_1)
check_box_menu_item_2 := CheckBoxMenuItem()
check_box_menu_item_2.set_label("Explosions")
check_box_menu_item_2.connect(self, "handle_v2_events", ACTION_EVENT)
check_box_menu_item_2.set_is_checked()
menu_2.add(check_box_menu_item_2)
menu_bar_1.add(menu_2)
menu_3 := Menu()
menu_3.set_label("Help")
text_menu_item_5 := TextMenuItem()
text_menu_item_5.set_label("Game Play...")
text_menu_item_5.connect(self, "handle_v2_events", ACTION_EVENT)
menu_3.add(text_menu_item_5)
menu_separator_1 := MenuSeparator()
menu_separator_1.set_is_shaded()
menu_3.add(menu_separator_1)
text_menu_item_4 := TextMenuItem()
text_menu_item_4.set_label("About...")
text_menu_item_4.connect(self, "handle_v2_events", ACTION_EVENT)
menu_3.add(text_menu_item_4)
menu_bar_1.add(menu_3)
self.add(menu_bar_1)
roids := Blasteroids_Game()
roids.set_pos("0", "26")
roids.set_size("100%", "100%-26")
self.add(roids)
self.connect(self, "handle_v2_events", &null)
end
method component_setup()
self.setup()
end
initially
optionsbox := optionsdialog()
doneoptions := 0
self.Dialog.initially()
end
procedure main()
local d
d := blaster()
d.show_modal()
end
### Ivib-v2 layout ##
#class|Canvas|17|Name Table|table|integer|0|6|string|check_box_menu_ite
#m|integer|2|string|custom|integer|1|string|menu|integer|3|string|menu_
#bar|integer|1|string|menu_separator|integer|1|string|text_menu_item|in
#teger|6|SuperClass Name|string|Dialog|Import Name|string|gui|Button Gr
#oups|class|ButtonGroupSet|2|Parent Canvas|1|Boxes|list|0|Checkbox Grou
#ps|class|CheckBoxGroupSet|2|Parent Canvas|1|Boxes|list|0|Gen Indent|in
#teger|3|Gen Main|integer|1|Gen Component Setup|integer|1|Gen Init Dial
#og|integer|1|Gen Initially|integer|1|Dialog Struct|class|CDialog|4|Min
# Width|null|Min Height|null|Ticker Rate|null|Attribs|list|1|string|bg=
#pale gray|Name|string|blaster|Width|integer|500|Height|integer|500|Ite
#ms|list|2|class|CanvasMenuBar|26|Parent Canvas|1|Name|string|menu_bar_
#1|Class Name|string|MenuBar|Import Name|string|gui|X Fix|null|Y Fix|nu
#ll|W Fix|null|H Fix|null|W Default|integer|1|H Default|integer|1|X Spe
#c|string|0|Y Spec|string|0|W Spec|string|100%|H Spec|integer|23|X Alig
#n|string|l|Y Align|string|t|Is shaded|null|Is Button Subclass|null|Dra
#w Border|null|Attribs|list|0|Tooltip|null|Accel|null|Event Handlers|nu
#ll|Class Variable|null|Parent Component|1|Menus|list|3|class|CanvasMen
#u|17|Name|string|menu_1|Class Name|string|Menu|Import Name|string|gui|
#Accel|null|Label|string|Game|Label Left|null|Label Right|null|Is shade
#d|null|Img Left|null|Img Left Width|null|Img Left Height|null|Img Righ
#t|string|9,c1,0~~~~~~~~000~~~~~~00000~~~~0000000~~0000000000000000~~00
#000~~~~000~~~~~~0~~~~~~~~|Img Right Width|integer|9|Img Right Height|i
#nteger|9|Event Method|null|Class Variable|null|Children|list|3|class|C
#anvasTextMenuItem|16|Name|string|text_menu_item_3|Class Name|string|Te
#xtMenuItem|Import Name|string|gui|Accel|null|Label|string|New|Label Le
#ft|null|Label Right|null|Is shaded|null|Img Left|null|Img Left Width|n
#ull|Img Left Height|null|Img Right|null|Img Right Width|null|Img Right
# Height|null|Event Method|string|handle_v2_events|Class Variable|integ
#er|1|class|CanvasTextMenuItem|16|Name|string|text_menu_item_2|Class Na
#me|string|TextMenuItem|Import Name|string|gui|Accel|null|Label|string|
#High Scores...|Label Left|null|Label Right|null|Is shaded|null|Img Lef
#t|null|Img Left Width|null|Img Left Height|null|Img Right|null|Img Rig
#ht Width|null|Img Right Height|null|Event Method|string|handle_v2_even
#ts|Class Variable|integer|1|class|CanvasTextMenuItem|16|Name|string|te
#xt_menu_item_1|Class Name|string|TextMenuItem|Import Name|string|gui|A
#ccel|null|Label|string|Quit|Label Left|null|Label Right|null|Is shaded
#|null|Img Left|null|Img Left Width|null|Img Left Height|null|Img Right
#|null|Img Right Width|null|Img Right Height|null|Event Method|string|h
#andle_v2_events|Class Variable|integer|1|class|CanvasMenu|17|Name|stri
#ng|menu_2|Class Name|string|Menu|Import Name|string|gui|Accel|null|Lab
#el|string|Options|Label Left|null|Label Right|null|Is shaded|null|Img
#Left|null|Img Left Width|null|Img Left Height|null|Img Right|string|9,
#c1,0~~~~~~~~000~~~~~~00000~~~~0000000~~0000000000000000~~00000~~~~000~
#~~~~~0~~~~~~~~|Img Right Width|integer|9|Img Right Height|integer|9|Ev
#ent Method|null|Class Variable|null|Children|list|3|class|CanvasTextMe
#nuItem|16|Name|string|text_menu_item_6|Class Name|string|TextMenuItem|
#Import Name|string|gui|Accel|null|Label|string|Games Options...|Label
#Left|null|Label Right|null|Is shaded|null|Img Left|null|Img Left Width
#|null|Img Left Height|null|Img Right|null|Img Right Width|null|Img Rig
#ht Height|null|Event Method|string|handle_v2_events|Class Variable|int
#eger|1|class|CanvasCheckBoxMenuItem|20|Name|string|check_box_menu_item
#_1|Class Name|string|CheckBoxMenuItem|Import Name|string|gui|Accel|nul
#l|Label|string|Sound|Label Left|null|Label Right|null|Is shaded|null|I
#mg Left|string|(b=black;0=shadow;1=hilite;~=trans)13,c6,~~~~~~~~~~~~~~
#00000000001~~00000000001~~00~~~~~~~11~~00~bbbbb~11~~00~bbbbb~11~~00~bb
#bbb~11~~00~bbbbb~11~~00~bbbbb~11~~00~~~~~~~11~~01111111111~~1111111111
#1~~~~~~~~~~~~~~|Img Left Width|integer|13|Img Left Height|integer|13|I
#mg Right|null|Img Right Width|null|Img Right Height|null|Event Method|
#string|handle_v2_events|Class Variable|integer|1|Img Up|string|(0=shad
#ow;1=hilite;~=trans)13,c6,~~~~~~~~~~~~~~11111111111~~11111111110~~11~~
#~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~
#00~~11~~~~~~~00~~11000000000~~10000000000~~~~~~~~~~~~~~|Img Down|strin
#g|(b=black;0=shadow;1=hilite;~=trans)13,c6,~~~~~~~~~~~~~~00000000001~~
#00000000001~~00~~~~~~~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~11~~00~bb
#bbb~11~~00~bbbbb~11~~00~~~~~~~11~~01111111111~~11111111111~~~~~~~~~~~~
#~~|Is Checked Flag|integer|1|Parent CheckBoxGroup|null|class|CanvasChe
#ckBoxMenuItem|20|Name|string|check_box_menu_item_2|Class Name|string|C
#heckBoxMenuItem|Import Name|string|gui|Accel|null|Label|string|Explosi
#ons|Label Left|null|Label Right|null|Is shaded|null|Img Left|string|(b
#=black;0=shadow;1=hilite;~=trans)13,c6,~~~~~~~~~~~~~~00000000001~~0000
#0000001~~00~~~~~~~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~
#11~~00~bbbbb~11~~00~~~~~~~11~~01111111111~~11111111111~~~~~~~~~~~~~~|I
#mg Left Width|integer|13|Img Left Height|integer|13|Img Right|null|Img
# Right Width|null|Img Right Height|null|Event Method|string|handle_v2_
#events|Class Variable|integer|1|Img Up|string|(0=shadow;1=hilite;~=tra
#ns)13,c6,~~~~~~~~~~~~~~11111111111~~11111111110~~11~~~~~~~00~~11~~~~~~
#~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~11~~~~~~~00~~
#11000000000~~10000000000~~~~~~~~~~~~~~|Img Down|string|(b=black;0=shad
#ow;1=hilite;~=trans)13,c6,~~~~~~~~~~~~~~00000000001~~00000000001~~00~~
#~~~~~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~11~~00~bbbbb~
#11~~00~~~~~~~11~~01111111111~~11111111111~~~~~~~~~~~~~~|Is Checked Fla
#g|integer|1|Parent CheckBoxGroup|null|class|CanvasMenu|17|Name|string|
#menu_3|Class Name|string|Menu|Import Name|string|gui|Accel|null|Label|
#string|Help|Label Left|null|Label Right|null|Is shaded|null|Img Left|n
#ull|Img Left Width|null|Img Left Height|null|Img Right|string|9,c1,0~~
#~~~~~~000~~~~~~00000~~~~0000000~~0000000000000000~~00000~~~~000~~~~~~0
#~~~~~~~~|Img Right Width|integer|9|Img Right Height|integer|9|Event Me
#thod|null|Class Variable|null|Children|list|3|class|CanvasTextMenuItem
#|16|Name|string|text_menu_item_5|Class Name|string|TextMenuItem|Import
# Name|string|gui|Accel|null|Label|string|Game Play...|Label Left|null|
#Label Right|null|Is shaded|null|Img Left|null|Img Left Width|null|Img
#Left Height|null|Img Right|null|Img Right Width|null|Img Right Height|
#null|Event Method|string|handle_v2_events|Class Variable|integer|1|cla
#ss|CanvasMenuSeparator|16|Name|string|menu_separator_1|Class Name|stri
#ng|MenuSeparator|Import Name|string|gui|Accel|null|Label|null|Label Le
#ft|null|Label Right|null|Is shaded|integer|1|Img Left|null|Img Left Wi
#dth|null|Img Left Height|null|Img Right|null|Img Right Width|null|Img
#Right Height|null|Event Method|null|Class Variable|null|class|CanvasTe
#xtMenuItem|16|Name|string|text_menu_item_4|Class Name|string|TextMenuI
#tem|Import Name|string|gui|Accel|null|Label|string|About...|Label Left
#|null|Label Right|null|Is shaded|null|Img Left|null|Img Left Width|nul
#l|Img Left Height|null|Img Right|null|Img Right Width|null|Img Right H
#eight|null|Event Method|string|handle_v2_events|Class Variable|integer
#|1|class|CanvasCustom|25|Parent Canvas|1|Name|string|roids|Class Name|
#string|Blasteroids_Game|Import Name|string|gui|X Fix|null|Y Fix|null|W
# Fix|null|H Fix|null|W Default|null|H Default|null|X Spec|string|0|Y S
#pec|string|26|W Spec|string|100%|H Spec|string|100%-26|X Align|string|
#l|Y Align|string|t|Is shaded|null|Is Button Subclass|null|Draw Border|
#null|Attribs|list|0|Tooltip|null|Accel|null|Event Handlers|list|0|Clas
#s Variable|integer|1|Parent Component|1|Initial Focus|null|Event Handl
#ers|list|1|list|2|string|&null|string|handle_v2_events|
Lecture 10
Clarification of the size of worlds
- According to
wiki answers: earth's radius is 3961 miles; its circumference is 25k+. Thanks Bruce.
- According to
a battle.net forum
one of the many estimates of the size of the World of Warcraft is: around 8
miles in radius.
- Accepting that it is scaled for our convenience from some fictional planet
that is comparable in size to earth, the "scaling for our convenience" is
~500x. As far as I know that is not counting the Outland you can port to, which
is a set of fragments of the Orcs' home world.
- It is reasonable to ask questions like: is 500x the optimal scaling
number for virtual worlds? Suppose that people want more content -- more
surface area? What additional game mechanics would that call for?
- Where scaling gets interesting is when scaling of sizes/distances
triggers issues in scaling of time -- real time or game time.
- Lotro abstracts away time/space trade-offs using "Travel Rations".
You buy and eat travel rations to ignore months-long travel. Are there
better ways to accomplish such scale-tweaks?
-
When gamers want not just a virtual planet, but a virtual galaxy,
what are the implications for a virtual environment? How successful
have games been at feeling "galactic" in scope?
- WoW doesn't feel like it is a 500x scale.
"Scale" in games might be 1:1 in some areas, 50x or 500x in others, or
logarithmic if you have a really large space to deal with. See
research papers on "graphical fisheye views of graphs" for dynamic
nonlinear scales one could adopt in games.
- Suppose a virtual world designer wanted to dynamically tweak the
scale as new content is introduced? Suppose a game mechanic
included a "Zoom" mechanism? What might it look like?
Rabin
Chapter 3.4 is once again software engineering review material, which may
be more useful for those of you who haven't taken CS 383-384 from me. Feel
free to ask questions about things that don't make sense! The rest of you:
look for any nuggets of interest.
Blasteroids, cont'd
Last lecture we got somewhere approaching halfway through
the Blasteroids program with commentary before we
ran out of time. 1500 lines is admittedly a lot, and we have many subjects
more interesting than how to program 2D wireframe asteroids. So we must skim.
While we are at it, we might ask: what parts are unclear, and what parts
could we do different and better than Jared Kuhn managed at the time?
game.icn
The guts of the Blasteroids game is a 756-line module that is effectively
the arcade game hidden inside its GUI walls. It has a lot of $define's
for major game parameters. Note the $include to keysyms.icn, that Unicon
library include file defines symbols for many interesting keys on the
keyboard. Blasteroids includes it in order to use arrow keys.
import gui
$include "keysyms.icn"
$define MAXHEIGHT 572 #playfield height/width dimensions
$define MAXWIDTH 500
$define VOFFSET 60
$define VSTARSOFF VOFFSET-36
$define SPEEDINC 0.1 #acceleration constant
$define MAXSPEED 5.0 #maximum speed
$define NEGMAXSPEED -5.0 #negative of max speed
$define RPTMIN 7
$define RMINSIZE 15 #min asteroid size (pixels)
$define RMAXSIZE 30 #max asteroid size (pixels)
$define ROIDPROBABILITY 960 # interval of new asteroids (lower=more)
$define MAXROIDS 10 #maximum allowed number of asteroids
$define RMAXSPEED 2.5 #maximum speed of asteroids
$define MAXBLASTS 10 #no more than this many laser blasts at a time
$define MAXBLASTRANGE 40 #how far the blasts travel before they fizzle out
$define BLAST_RADIUS 36 #how far away a legal collision is from an asteroid
$define DELAY_TIME 33 #length of a timeslice (milliseconds)
The game uses the record types, for things too simple to warrant a class.
When to use records: with SQL/ODBC, or when a set of associated data has
no significant associated behavior. Otherwise, you should use classes.
Candid admission: if I wrote the code below, personally I would probably
use a class for the asteroids (rtrack) but not for the points.
Probably a class for the gun blasts, too.
record point(x,y) #a point on the screen
record rtrack( #asteroid tracking record
x, y, #center coordinates
angle, #angle of rotation (not used)
points, #array polygon point coordinates (relative to the center)
size, speed, #size / speed of asteroid
offset, #precomputed multiplier, saves computations
offset2x, #same
active) #boolean, active or not
record btrack( #blast tracking record
x, y, #center coordinates
angle, #angle of rotation of blast
flag, #active / inactive
traveled, #how far the blast has gone
deactnext) #flag this to be deactivated next time slice
Class Blasteroids is a hand-written GUI Component. It has a lot of
fields (member variables) and is basically a model, view and controller
all in one. Variables are named well but under-commented. It has
poor cohesion and is an example of an AntiPattern that should be
refactored.
class Blasteroids_Game : Component(
starsdone, # initialization flag
tapped,
gamestart,
shipx, shipy,
c,
newwin, backwin,
lookups, lookupc,
currblast, blasts, blastwin,
roids, currroids, roidwin,
explodewins, shiphit,
speedx, speedy,
angle, currspeed,
oldshipx, oldshipy,
masterspeed, oldangle,
negx, negy,
redrawship,
x1, y1,
firsttime,
livesleft, livesdone,
score, oldscore,
num_ships, num_range,
chk_explode, chk_sound,
num_torpedos, num_speed,
num_rotation, num_level,
left_pressed, right_pressed,
up_pressed, down_pressed)
To draw yourself, you draw on your window. But there is an off-screen
backup copy of the static star images, used to erase things.
method display()
if starsdone = 0 then {
drawstars()
newwin := cwin
explodewins := []
every put(explodewins, Clone(newwin,"fg=" ||
("red"|"orange"|"orangish red"|"black"|"white")))
&window := newwin
CopyArea(newwin, backwin, 0, 0, MAXWIDTH, MAXHEIGHT)
}
end
method drawstars()
starclones := []
every put(starclones, Clone(cwin,"fg=" ||
("vivid white"|"gray"|"light gray"|"dark gray"|"black")))
FillRectangle(starclones[5], 0, VSTARSOFF, MAXWIDTH, MAXHEIGHT)
#at every point...
every (x_loc := 0 to MAXWIDTH, y_loc := 0 to 3) do {
#draw a randomly colored star in a random location
DrawPoint(starclones[(?100-1)/25+1], x_loc, ?500 + VOFFSET)
}
newwin := cwin
CopyArea(newwin, backwin, 0, 0, MAXWIDTH, MAXHEIGHT)
starsdone := 1
end
There is a bunch of boring initialization...
method reset_everything()
eraseship()
eraseblasts()
eraseroids()
currblast := 0
currroids := 0
redrawship := 5
x1 := (MAXWIDTH / 2)
shipx := (MAXWIDTH / 2)
y1 := (MAXHEIGHT / 2)
shipy := (MAXHEIGHT / 2)
#and going nowhere
speedx := 0
speedy := 0
masterspeed := 0
oldshipx := 0
oldshipy := 0
#flag, (0 or 1), if the ship is drawn, the delay has already been
# done, otherwise, we delay when the blasts / asteroids are done
# number of times to redraw the ship when it isn't moving
redrawship := 5
#angle of the ship (true angle = angle -1)
angle := 1
oldangle := 0
firsttime := 1
livesleft := 5
livesdone := 0
starsdone := 0
if tapped = 1 then gamestart := 0
tapped := 0
score := 0
oldscore := -1
#kill any straggling asteroids or blasts
every blasts[1 to MAXBLASTS].flag := 0
every roids[1 to MAXROIDS].active := 0
end
Juicy event handling
method handle_event(e)
local pqz
if tapped = 1 then {
tapped := 0
reset_everything()
drawstars()
}
if firsttime = 1 & starsdone = 1 then {
firsttime := 0
}
if gamestart = 1 then {
case e of {
" ": {
#space bar, same as left click
if shiphit = 0 then {
redrawship := 5
eraseblasts()
eraseship()
initblast(x1, y1, angle - 1)
eraseblasts()
drawship(x1, y1, angle)
}
}
"d" | "D" : {
Bg("black")
Fg("white")
GotoRC(10, 1)
WWrite(cwin, " ")
GotoRC(10, 1)
WWrite(cwin, "Here: ", num_ships, " ", num_level,
" ", num_rotation)
}
"q" | "Q": {
exit()
}
Key_Left: {
angle -:= 5
if(angle <= 0) then angle +:= 360
left_pressed := 1
}
Key_Right: {
angle +:= 5
if(angle > 360) then angle -:= 360
right_pressed := 1
}
-(Key_Left)-128: {
left_pressed := &null
}
-(Key_Right)-128: {
right_pressed := &null
}
-(Key_Up)-128: {
up_pressed := &null
}
-(Key_Down)-128: {
down_pressed := &null
}
Key_Up: {
speedx +:= lookupc[angle] * SPEEDINC
speedy +:= lookups[angle] * SPEEDINC
if speedx > MAXSPEED then speedx := MAXSPEED
if speedx < (NEGMAXSPEED) then speedx := NEGMAXSPEED
if speedy > MAXSPEED then speedy := MAXSPEED
if speedy < (NEGMAXSPEED) then speedy := NEGMAXSPEED
up_pressed := 1
}
Key_Down: {
speedx -:= lookupc[angle] * SPEEDINC
speedy -:= lookups[angle] * SPEEDINC
if(speedx > MAXSPEED) then speedx := MAXSPEED
if(speedx < (NEGMAXSPEED)) then speedx := NEGMAXSPEED
if(speedy > MAXSPEED) then speedy := MAXSPEED
if(speedy < (NEGMAXSPEED)) then speedy := NEGMAXSPEED
down_pressed := 1
}
}
}
end
The idle function was wired into the GUI event dispatcher by means of a
"ticker". Every GUI Component is a ticker
method idle_func()
local px, py, maxspeed, dist
local doskip, intdist, delayed, starclones, temp
# don't start until they say New Game
if not (\gamestart=1) then fail
delayed := 0
# redrawship := 1
#everything is drawn in white, may change later on
Fg("white")
if livesdone=0 then {
every pqz := 1 to livesleft do {
drawship2((((pqz-1)*34)+18) , 42)
}
}
#game loop
while *Pending() = 0 do {
if score ~= oldscore then {
oldscore := score
Bg("black")
GotoRC(4, 30)
WWrite(cwin, score)
}
if (shipx~= x1 | shipy ~= y1 | oldangle ~= angle) & (shiphit = 0)then{
#we're redrawing it here, no need to elsewhere
redrawship := 0
oldangle := angle
#finally, draw the ship
eraseblasts()
drawship(x1, y1, angle-1)
drawblasts()
WDelay(DELAY_TIME)
#already did delay, don't do it again
delayed := 1
}
else if shiphit > 0 then {
redrawship := doexplodeship()
x1 := MAXWIDTH / 2
y1 := MAXHEIGHT / 2
}
x1 +:= speedx
y1 +:= speedy
if(x1 > MAXWIDTH - 20) then x1 := 20
if(x1 < 20) then x1 := MAXWIDTH - 20
if(y1 > MAXHEIGHT - 20) then y1 := 20 + VOFFSET
if(y1 < (20 + VOFFSET)) then y1 := MAXHEIGHT - 20
#randomly put in a new asteroid, up to the max
#may change rules for this based on a kind of level system
if currroids < MAXROIDS then {
if ?1000 > ROIDPROBABILITY then {
eraseblasts()
# eraseship()
eraseroids()
initroid()
eraseroids()
# drawship(x1, y1, angle-1)
drawblasts()
}
}
#thinking I ought to split these boys up, would save on if statements
if currblast > 0 | currroids > 0 then {
if currblast > 0 then eraseblasts()
if currroids > 0 then eraseroids()
if redrawship > 0 then eraseship()
if currroids > 0 then animroids()
if currblast > 0 then animblasts()
if redrawship > 0 then {
drawship(x1, y1, angle-1)
redrawship -:= 1
}
if currroids > 0 then drawroids()
if currblast > 0 then drawblasts()
if delayed = 0 then WDelay(DELAY_TIME)
else delayed := 0
}
if \left_pressed then
handle_event(Key_Left)
if \right_pressed then
handle_event(Key_Right)
if \up_pressed then
handle_event(Key_Up)
if \down_pressed then
handle_event(Key_Down)
}
#aha! they did something!
end
Conclusions: Blasteroids does a lot right, but could benefit from
further polish. It serves as a useful example.
Strategy games
In the old days, there was this awesome magazine called
"Strategy & Tactics". Every couple months you could
get a new issue. They generally came with a new strategy
game each time. A new map. New (cardboard counter) units.
New rules.
A new virtual world in which to conduct strategic battles.
What is strategy? What is tactics?
- strategy
- a plan. how to string the battles together in order to win the war.
example: we will win the war by blockading the enemy's ports
- tactics
- techniques for using weapons/units in combination to win a battle.
example: we will win the battle by convincing the enemy that we are
far more numerous and confusing them.
Real Time vs. Turn-based
While Generals and battle commanders must function in real-time, they
often rehearse their plans for hours, trying to test out all the unknowns,
especially variations in enemy strength and behavior. While real battles
involve massive-scale concurrent behavior on a continuous clock,
simulations may use fixed time-slices and allow
each unit to perform a selection of appropriate actions during their
time slice.
Real-time Strategy Games are usually a misnomer. The actions of
players of real-time strategy games are usually tactics.
Example Old-School Tabletop Turn-Based Strategy Games
- Empire (ascii art) (description)
- Ogre and G.E.V. (microgames)
- Civilization (there was a boardgame prior to Sid Meier's PC games, but
Sid Meier's Civilization is quite a bit different)
- SPI and Avalon Hill Games (detailed simulations of the most famous
battles in history, Civil War, WW II, etc.)
Example Turn-Based Strategy Computer Games
Lots of tabletop games have been implemented on computers.
Also, you might want to check out.
- battle for wesnoth
- ruse -- wwii -- scales from individual soldiers to miles-and-miles
- age of empires -- balancing, extensible unit behavior
- stronghold -- defensive, plan to withstand the onslaught
- simcity -- non-combat
- sins of a solar empire
Example Real-Time Strategy Games
This subgenre first appeared on computers. Pros: time slices are
tiny, in real life you can't micromanage thousands of units' behavior
each time slice. Nice real-time graphics animations. Cons: relies
very heavily on AI, even for human players.
- Warcraft (not World-of, just Warcraft)
- Dune II, Command and Conquer
- Dozens of me-too games, including many great ones
Writing your own Ogre game
First off, what would it take to simply draw the map? We
start with a class for drawing hexes, similar to what is given below.
class Hex(row, col, # integer row and column #, used in x,y calculations
terraintype, # for ogre: "normal" and "impassable"
units, # list of (0-or-1, or 0-or-more, check rules) unit objs
exits) # list of 6 adjacent hexes
method draw()
# draw the terrain
if terraintype == "impassable" then {
Pattern()
}
FillPolygon( vertices... )
# draw the edges
DrawLine(vertices...)
every i := 1 to 6 do {
if exits[i] === &null then {
# draw the impassable edge
}
}
# draw the units
if *units > 0 then # draw what is on the top of the stack
units[1].draw()
if *units > 1 then # draw "multi-unit stack" decoration
end
end
Lecture 11.
Guest Discussion with Dr. Ian Chambers
Professor Chambers teaches "Pirates of the Caribbean" in the UI History
department. He will join us for an open discussion session on pirate games.
His e-mail is chambers@uidaho.edu; feel free to ask questions off-line.
Please identify what class you are with when you do. :-)
Recommended comprehensive pirate sites:
Dr. Chambers' recommended pirates and scenarios:
- Blackbeard
- swam headless around the whole ship. boss battles were not invented
by videogame designers in the 1980's
- Julius Caesar
- ultimate revenge scenario: sought out and destroyed his pirate captors
- Henry Morgan
- note the cows and nuns as weapons
- Treasure Island
- multiple locations, long-voyage-plot, hunt for buried treasure, the works
Open discussion / brainstorming
Rabin of the Day
Ogre
Show and Tell
- Why Ogre?
- The Metagaming Microgames are nearly perfect examples
of small-scale games that could be fully implemented in not many lines of
code.
- sequel G.E.V.
- adds many near-future unit types, terrain effects, and scenarios
- Other microgames
- hand-to-hand combat games Melee and Wizard were precursors to GURPS
- Designed by Steve Jackson
- Later founded Steve Jackson Games and the famous Munchkin card games.
- Turn-based
- Focus on the simulation mechanics, not twitch skills; would be fun
challenge to carefully adapt to real-time.
- All About Tactics
- Microgames take only a few minutes to play, don't involve strategy much.
Hard to come up with a "small scale strategy game" that's really strategy.
Ogre: Now What?
Besides graphics rendering of the mapboard and pieces, what will an
Ogre strategy game have to do?
- Unit class
- class representation for Units, including Ogres. Let's design it now.
- user input
- translation of mouse actions on the map into events on (row,col)
hex locations.
- execution of game rules
- turn sequence; movement and combat
- AI
- The game can be two-player with no AI, but the most fun scenario
is where the computer plays the Ogre and you the puny human plays
the humans trying to survive. Alternately, you could be the Ogre,
and let the computer try to survive.
Lecture 12. Strategy Games, cont'd
Double Header?
Dr. J seminar after class at 3:30 in TLC 44 or so.
Grading Update
I have made progress on grading; saw a nice C++/SDL solution.
Next Virtual Environment
If you do not have one already,
please download "SecondLife" from
Linden Labs' secondlife site
and create an free account. We will hold a session in SecondLife in
the near future.
Rabin of the Day
Checkers
In a previous edition of this class, one of the students wrote a very nice
checkers game, complete with a reasonably smart A/I computer player.
The algorithms used to optimize chess and checkers can be
adapted to other strategy games to some extent. The Artificial Intelligence
class is not a prerequisite for this one, so if you know it, use it, and if
you don't know it, consider this a gentle introduction.
This material adapted from wikipedia, so it must be true.
There is a nice
visualization of minimax available.
Given a board position P we wish to calculate our best possible move.
There is a
tree of board positions which allows us to evaluate all
possible moves*.
From the current position we select the next move
as the child whose board position is best (maximizing our position).
But our evaluation of
board positions considers further moves (deeper "look ahead") as much as
CPU allows. We can reasonably assume our adversary will make their best
possible move (minimizing our position).
Our best position, short of forcing a win, will be whatever gives the
opponent the least opportunity.
procedure minimax(node, depth)
if gameover(node) | (depth = 0) then {
return evaluate(node)
}
else {
α := -&infinity;
every child := possiblemoves(node) do {
α <:= -minimax(child, depth-1)
}
return α
end
Good: allows perfect play
Bad: may require infinite compute resources
is an optimization to reduce
(somewhat) the combinatorial explosion of minimax.
The basic notion is that we can discard parts of the tree when we know
they cannot alter the result. Analogy: short-circuit boolean operators
skip right-hand sides once it is known they cannot change the outcome.
Lecture 13. Minimax, Strategy Games
HW#2 Status Update
Still grading. Just as well that you got an extension on HW#3.
Solutions ranged from 300 lines to 3000 lines. Degree of functionality
varies widely. Grades will be based more on observable mechanics and
coding style than on eye candy, but obviously eye candy counts a bit.
HW#2 was a warmup and for some it provided a good basis for HW#3, or at
least a prep on all the libraries and graphics skills needed.
Comment on Minecraft vs. SecondLife
- Some students requested Minecraft instead of SL,
claiming it is a multiuser virtual environment already.
- Minecraft is popular but it is not free.
- I think it would be good to be familiar with both MC and SL in this class
- Point of next virtual environment study is world-building.
- Compared to LOTRO, both MC and SL have the fatal flaw of not having
a coherent narrative other than "build stuff".
Rabin
Chapter 3.7, Memory and I/O.
Tic-Tac-Toe and the Minimax Algorithm
We need an explicit representation of the board. We could do it as a
list of lists of strings, with " " for unused, "x" for x, and "o" for o.
L := [ [" ", " ", " "],
[" ", " ", " "],
[" ", " ", " "] ]
Although this has appealing ascii-art properties,
(ironically?) while playing with the evaluation functions, I found
it useful to use numerically-based representation, with the different
player marks represented by different primes, say 2 for "x" and 3 for "o".
L := [ [1,1,1], [1,1,1], [1,1,1] ]
Using this representation, you can tell if you have 2 or 3 in a row,
and whether a row is blocked by the enemy, by multiplying the three
numbers together.
We need an evaluation function. By definition it is a heuristic
function, although with simple games and extreme cases it may be that the
heuristic can be proved to produce optimal results.
procedure evaluate(L, player)
if player=="x" then { player := 2; enemy := 3 }
else { player := 3; enemy := 2 }
# absolute results
# 1000 if there exists a 3-in-a-row
# -1000 if there exists an enemy 3-in-a-row
if numNinaRow(L, player, 3)>0 then return 1000
if numNinaRow(L, enemy, 3)>0 then return -1000
# heuristic opinions
sum := 0
# + 100 for each (unblocked) 2-in-a-row
# - 100 for each enemy (unblocked) 2-in-a-row
sum +:= 100 * numNinaRow(L, player, 2)
sum -:= 100 * numNinaRow(L, enemy, 2)
# + 10 if we hold the center square
# - 10 if we hold the center square
if player = L[2,2] then sum +:= 10
if enemy = L[2,2] then sum -:= 10
# + 2 for each corner square
# - 2 for each enemy corner square
if player = L[1,1] then sum +:= 2
if player = L[1,3] then sum +:= 2
if player = L[3,1] then sum +:= 2
if player = L[3,3] then sum +:= 2
if enemy = L[1,1] then sum -:= 2
if enemy = L[1,3] then sum -:= 2
if enemy = L[3,1] then sum -:= 2
if enemy = L[3,3] then sum -:= 2
return sum
end
Counting how many N-in-a-row might look like:
procedure numNinaRow(L, player, n)
sum := 0
n := player ^ n
every i := 1 to 3 do {
if L[1,i] * L[2,i] * L[3,i] = n then sum +:= 1
if L[i,1] * L[i,2] * L[i,3] = n then sum +:= 1
}
if L[1,1] * L[2,2] * L[3,3] = n then sum +:= 1
if L[1,3] * L[2,2] * L[3,1] = n then sum +:= 1
return sum
end
Extending Minimax to RTS or MMO
- Minimax is a centralized intelligence
- Different possible ways to develop concurrent plans for all units
- Sauron-style -- a concurrent minimax could prioritize all units and
calculate deeper for important ones, and calculate others with
knowledge of what the other units are doing, especially neighbors
and important units
- A possibly-more-realistic concurrent minimax might let each unit
calculate what it is doing independently.
- Centralized management could be applied via dynamically changing
each unit's evaluation function
Comments on Monsters and NPC's
- Monsters and non-player characters need some kind of execution model.
- Moving past "Cellular Automata" such as "Life"...
- Many games use "good ole" finite automata (wander mode, sleep mode,
fight mode, flight mode...)
- Good execution models need to (look and) "feel" intelligent.
- You can certainly apply a variant of a minimax algorithm, especially if
a central intelligence controlling many units (Sauron)
- Is the goal in a game to appear human-like in intelligence?
- Behavior Trees are a model with alleged advantages over finite automata
- BDI (belief-desire-intention) is a popular human-like AI model.
- Build a tree data structure to organize complex unit behavior rules.
- Different node types for prioritized, sequenced, conditional and
concurrent/alternating behaviors
BDI
- Michael Bratman
- desire+"commitment"=intention
- selection of plans from a "plan library"
- balance (re)selection time vs. execution time
- execute the current plan until completion or commitment timeout
- does not include how to create plans
Lecture 14. Math
How are your HW#3's doing?
- I received one turnin last night
- I suspect most folks are still working furiously on their HW's
- I haven't received a lot of requests for appointments or questions
by e-mail.
- You need to get something reasonable working and turned in soon
Where we are at in the course
- Week 4 and 5 Notes
- here and here
- No one volunteered for taking this week
- any volunteers? A small number of points will be associated with
your note taking, so be sure to sign up (except Idaho Falls folks)
- schedule says last week we should have hit game math & physics
- we didn't, we are a bit behind
- and this week we should be talking graphics and virtual environments.
- we will
- HW#2
- I did more grading over the weekend, but my apologies:
given a choice between preparing for class or grading, I prepare for class
- Strategy Game code (ogre) example
- The Ogre program is available at
http://www2.cs.uidaho.edu/~jeffery/courses/428/ogre-v0/ and as
I said before, it is a semi-playable useful example of computerizing
a manual strategy game, but needs
a lot of refinement.
- Second Life
- Please work your way through the tutorial island and head to Dr. J's
place. Can we do a session in Second Life by tomorrow night?
Secondlife.com handles user account creation; a tiny link at the bottom
says "Downloads". Last I looked, It offers Windows, OSX, and Linux binaries.
I am going to assume that we have no problems with you getting a working
SecondLife client. If you do have a problem, let me know ASAP. My SL
user name is Jeffery Clinton.
- Unicon
- Unicon has already been updated once this semester and another update
is forthcoming.
Rabin of the Day
-
Chapter 4.1, Mathematical Concepts.
- This feels a lot like CS 324 review material, but for most of you there
are one or more elements that are new or need brushing up or dusting off.
Parallel Concerns, Moving into Virtual Environments
- learn enough Unicon to do the VE assignments
- learn about 3D programming
- learn about 3D modeling
- start assembling some "pirate assets"
- try out SecondLife's (and/or MineCraft's) content creation
- introduce CVE
We are gonna probably go breadthfirst through these to provide some coverage
of all of them, rather than depthfirst to cover some and skip others.
Raw Data
This means: measurements and images from our department. If we had CAD
files for JEB that would be swell, but as far as I know, we don't. We have
crude floor plans. We need to extract (x,y,z) coordinates
for those portions of the building we wish to model, sufficient to make a
"wire frame" model. We need to create images depicting the surfaces of all
the polygons in that model, the images are called textures and have certain
special properties.
There is one other kind of raw data I'd like you to collect: yourselves.
I want to push beyond the crude avatars I've used previously, and model
ourselves in crude, low-polygon textured glory.
On Coordinate Systems
The coordinate system is the first piece of 3D graphics we are learning.
It is very simple and pretty much follows OpenGL conventions. x is east-west,
y is up-down, and z is north-south. Positive runs east, up, and south.
To anyone who gets confused about a positive axis running from right to left
or from top to bottom instead of what you were expecting: this just means
that your perspective is turned around from those used by the world
coordinates. If your character rotates 180 degrees appropriately, suddenly
positive values go the opposite direction from before and what was right to
left is the more familiar left to right. The point: world coordinates are
different from your personal eyeball coordinate system, don't confuse them.
A Common Coordinate System
For this class, we will use a standard/common coordinate system: 1.0 units =
1 meter, with an origin (0.0,0.0,0.0) in the northwest corner at ground
level. Y grows "up", X grows east, and Z grows south. The entire building
(except anything below ground level as viewed from the northwest corner)
will have fairly small positive real numbers in the model. This coordinate
system is referred to as FHN world coordinates (FHN=Frank Harary
Normal). Frank Harary was a graph theorist friend I knew in New Mexico.
The coordinate system is named after him because (0,0,0) was at one time
the corner of his office.
Room Modeling
For simplicity's sake, a room will consist of one or more rectangular
areas, each bounded by floor, ceiling, walls, and doors or openings into
other rectangular areas. Fortunately or unfortunately for you, we will
use the term Room to denote these rectangular areas. Within each room
are 0 or more obstacles and decorations. Obstacles are things like tables
and chairs, computers and printers. Decorations are things like signs
and posters that do not affect movement.
For your room, we need to:
measure its x and z using FHN,
- measure its y (how?)
- identify its exits (doors and openings) and for each exit,
- its connecting room
- its x,y,z, height, and plane
- collect textures for floor, walls, ceilings, doors, decorations
- identify major obstacles (essential virtual objects in the room)
Another Sample Room
Taken from NMSU's virtual CS department we have the following. It is for
a ground floor room (y's are 0). This example has both obstacles and
decorations.
Room {
name SH 167
x 29.2
y 0
z 0.2
w 6
h 3.05
l 3.7
floor Wall {
texture floor2.gif
coords [29.2,0,0.2, 29.2,0,3.9, 35.2,0,3.9, 35.2,0,0.2]
}
obstacles [
Box { # column
Wall {coords [34.3,0,0.2, 34.3,3.05,0.2, 34.3,3.05,0.6, 34.3,0,0.6]}
Wall {coords [34.3,0,0.6, 34.0,0,0.6, 34.0,3.05,0.6, 34.3,3.05,0.6]}
Wall {coords [34.0,0,0.6, 34.0,3.05,0.6, 34.0,3.05,0.2, 34.0,0,0.2]}
}
Box { # window sill
Wall {coords [29.2,0,0.22, 29.2,1.0,0.22, 35.2,1.0,0.22, 35.2,0,0.22]}
Wall {coords [29.2,1,0.22, 29.2,1.0,0.2, 35.2,1.0,0.2, 35.2,1,0.22]}
}
Chair {
coords [31.2,0,1.4]
position 0
color red
type office
movable true
}
Table {
coords [31.4,0,2.4]
position 180
color very dark brown
type office
}
]
decorations [
Wall { # please window
texture wall2.gif
coords [29.2,1.0,0.22, 29.2,3.2,0.22, 35.2,3.2,0.22, 35.2,1.0,0.22]
}
Wall { # whiteboard
texture whiteboard.gif
coords [29.3,1.0,3.7, 29.3,2.5,3.7, 29.3,2.5,0.4, 29.3,1.0,0.4]
}
Windowblinds {
coords [29.2,1.5,0.6]
angle 90
crod blue
cblinds very dark purplish brown
height 3.05
width 6
}
]
}
Lecture 15. 3D Graphics, part 1
HW#2 Update
Grading guide: 9 points possible.
Points | Letter equivalent
|
8,9 | A-, A
|
6,7 | B-, B
|
HW#4: a short homework
Warming up for the virtual environments part of the course.
Rabin: 3D Graphics
-
Chapter 5.1, Graphics.
(backup).
- a slight re-ordering; we will come back to
Chapter 4.2, Collision Detection but 3D graphics comes first.
We did approximiately the first 60 slides of the Rabin 5.1 graphics.
Lecture 16. Unicon 3D facilities; jeb demo
Where we are at
- Week 7 Notes
- As of 11:40am today, I have received HW#3's from 11 students, about
2/3rds of the class. If you haven't turned in a HW#3, I want a status report.
- Last time we spent the whole lecture on 3D fundamentals.
- Today we will do more concrete examples, and we will just push
Rabin to the back burner since he used the whole class last Thursday.
- I would like to schedule a secondlife session this week.
When would be best?
- On Thursday, there will be a take-home midterm exam posted.
- Grad students Jafar and Hani and myself have worked very hard to get out
a new version of Unicon with various fixes and usability improvements for
your viewing pleasure.
- Version 11.8 was a bugfix release, which had performance issues we fixed
- Version 11.9 (current) seems to work well on Win7 but we are looking at
tweaking it (to 11.9.1 or whatever) to run better on XP if you need that.
- Version 12.0 is the Big New Thing we are in beta on, it adds
threads and concurrency as a generalization of co-expressions.
History of 3D Graphics in Unicon
- OpenGL, Direct3D, Java3D, and often even higher level toolkits like
OpenInventor have high learning curves, measured in months not days.
- For Unicon, we wanted Very High Level 3D, not just a "C/C++ binding".
- Design goals: resemble Unicon 2D facilities. Interoperate
where feasible. Simplify compared with OpenGL (no new types. 20 functions
instead of 250+).
- Undergraduate Math major Naomi Martinez developed 3D graphics in Unicon
around 2003. Dr. J ported it to Windows the summer after Naomi did it
on Linux. It has since been improved by several other students.
- Ended up with 16 new functions, 6 2D functions extended for 3D
- Current status: near-final. In need of further work on fonts, shaders,
dynamic textures.
- Documentation: UTR9b;
also summarized in the Unicon book.
- Class libraries: unicon's uni/3d subdirectory contains class libraries
by Jafar Al Gharaibeh for doing things like using 3D models (Microsoft .x
text format is our primary format).
Opening 3D Window
procedure main(av)
&window := open("gl attributes", "gl", "canvas=hidden")
write("glversion : ", WAttrib("glversion"))
write("glvendor : ", WAttrib("glvendor"))
write("glrenderer : ", WAttrib("glrenderer"))
end
Tells whether you have 3D facilities that will work, and what 3D
platform you are running on. WAttrib() and attributes are a
primary way to communicate with 3D subsystem, like in 2D.
Eye
Function Eye() and attributes eye/eyepos/eyedir/eyeup specify camera position
and orientation. Changing anything about the eye triggers a scene refresh
(as would an explicit Refresh() call).
Default is eyepos=0,0,0, eyedir=negative z axis, eyepos=0,1,0
Primitives
These are pretty much what OpenGL gives you, which I note is an
interesting comparison with what the hardware gives you. "slice" and
"ring" attributes control how many triangles you end up with to
approximate a complex "primitive" such as a sphere or torus. Set them
low enough (say, for an integrated graphics card) and your spheres will
be diamonds...
Transformation Stacks
- IdentityMatrix(), PushMatrix(), PopMatrix()
- Normal use: hierarchy of primitives with relative coordinate systems
- projection and modelview stacks, MatrixMode() switches
Lighting, Color, Materials
- OpenGL uses pretty generic/weak lighting model (Phong)
- Default lighting is generic, ambient
- Attributes light0-light7 are on or off
- Light values include diffuse, ambient, specular, and position
- Light syntax:
"{diffuse,ambient,specular} colorname"
- My impression is that "diffuse" gives most simple control, and the
effects of others is subtle.
- Example: WAttrib("light0=on, diffuse yellow; ambient gold") ...
- Standard X11 Colors are 48-bit (boo)
- Surface materials include diffuse, ambient, specular, and shininess
components, or they can be textured, or material+texture blend
- Example materials:
Fg("{diffuse,ambient,specular,emission} colorname")
Textures
- Attribute texmode can be on or off
- Example: WAttrib("texmode=on", "texture=map.gif"), WAttrib("texcoord=...")
- Also note: Texture(t) is more flexible than texture=... t can be a window
- Texture images have width, height that are powers of 2
- In Unicon, textures can be in-lined "image strings", filenames
of image files .gif (.jpg, .png), other windows.
- Texture coordinates are set via attribute texcoord, function Texcoord().
- Either texcoord=auto or x1,y1,x2,y2,... are mapped to vertices
of a subsequently called primitive such as FillPolygon().
- Texture coordinates > 1.0 imply repetitions of the image (tiling)
- Possible to blend textures and materials
- Possible (preliminary/unfinished) to "draw" directly on textures.
w := open("win1","gl","bg=light blue","size=256,256")
Fg(w, "emission pale grey")
PushMatrix(w)
Rotate(w, -5.0, 1.0, 0.0, 0.0)
DrawCylinder(w, 0.0, 0.575, -2.0, 0.15, 0.05, 0.17)
PopMatrix(w)
Fg(w, "diffuse grey; emission black")
PushMatrix(w)
Rotate(w, -5.0, 1.0, 0.0, 0.0)
DrawCylinder(w, 0, 0, -2.5, 0.7, 0.035, 0.035)
PopMatrix(w)
DrawTorus(w, 0.0, -0.22 -2.5, 0.03, 0.06)
DrawTorus(w, 0.0, 0.6, -2.5, 0.05, 0.03)
w2 := open("win2.icn","gl","bg=black","size=400,400")
WAttrib(w2, "texmode=on")
Texture(w2, w)
Fg(w2, "diffuse purple; ambient blue")
DrawCylinder(w2, 0, 0, -3.5, 1.2, 0.7, 0.7)
3D Selection
See the UTR about this, but WSection() allows you to specify a name for
sets of 3D primitives, for which you can then get reports when they are
clicked on, by turning on keyword &pick.
The jeb demos
The jeb (janssen engineering building) demos give us
a gentle introduction to
- Unicon3D
- 3D modeling
- the internals of the CVE virtual environment
This introduction to the CVE code is intended to allow you to see
some Unicon 3D graphics in action. You can think of it as the first
2500 lines of code pertaining to the 3D graphics; earlier when I
showed the main select() loop in dispatch.icn I was showing the first
k lines of code from the network point of view.
Our modest goals: understand some Unicon language features used,
consider how to Piraticize these demos, contemplate writing
equivalent (or better) demos in your preferred language(s),
planning to extend to networking soon.
Diving into Jeb1
File Organization Comes First.
It appears we have two source files, and a few images (.gif) and model
(.dat) files.
jeb1: jeb1.icn model.u
unicon jeb1 model.u
model.u: model.icn
unicon -c model
jeb1.zip: jeb1.icn model.icn
zip jeb1.zip jeb1.icn model.icn *.gif *.dat makefile README
jeb1.icn
- ~500 lines. If you want to reimplement in your preferred language,
go for it. Down the road this has to include TCP networking.
- IVIB-generated GUI front end
- Embeds a 3D subwindow in a larger 2D window
- class Subwindow3D is a bit of a hack but shows technique
- Dispatch message loop overridden for subwin and net
- include-in-example modelfile parser should be outsourced.
A Walk Through a Smattering of Jeb1
Most attributes can be changed afterwards using function WAttrib(),
which takes as many attributes as you like. The following line
enables texture mapping in the window:
WAttrib("texmode=on")
Assigning a value to a variable uses := in Unicon. Most of the rest of
the numeric operators and computation is the same as in any programming
language. In 3D graphics, a lot of real numbers are used.
In the jeb1 demo, the
user controls a moving camera, which has an (x,y,z) location, an
(x,y,z) vector describing where they are looking, relative to their
current position, and camera angles up or down. Initial values of
posx and posy are "in the middle of the first room in the model".
The rooms have min and max values for x,y, and z so:
posx := (r.minx + r.maxx) / 2
posy := r.miny + 1.9
posz := (r.minz + r.maxz) / 2
lookx := posx; looky := posy -0.15; lookz := 0.0
Unicon has a "list" data type for storing an ordered collection of items.
There is a global variable named Room which holds such a list, a list of
Room() objects. We will discuss Room() objects in a bit. This is how
the list of rooms is created, with 0 elements:
Rooms := [ ]
The code that actually reads a model file and creates Room() objects
and puts them on the Rooms list is procedure make_model(). We defer
the actual parsing discussion to later or elsewhere.
procedure make_model(corridor)
local fin, s, r
fin := open(modelfile) | stop("can't open model.dat")
while s := readlin(fin) do s ? {
if ="#" then next
else if ="Room" then {
r := parseroom(s, fin)
put(world.Rooms, r)
world.RoomsTable[r.name] := r
if /posx then {
# ... no posx defined, calculate posx/posy per earlier code
}
}
else if ="Door" then parsedoor(s,fin)
else if ="Opening" then parseopening(s,fin)
# else: we didn't know what to do with it, maybe its an error!
}
close(fin)
end
The "rooms" in jeb.dat are JEB230, JEB 228 and the corridor
immediately outside. Each room is created by a constructor procedure,
and inserted into both a list and a table for convenience access. (Do
we need the list? Maybe not! Tables are civilization!)
The following line steps through all the elements of the Rooms list,
and tells each Room() to draw itself. The exclamation point is an
operator that generates each element from the list if the surrounding
expression requires it. The "every" control structure requires every
result the expression can produce. The .render()
calls
a method render() on an object (in this case, on each object in turn as
it is produced by !). Note that our CVE will probably
want to get smart about only drawing those rooms that are "visible",
in order to scale performance.
every (!Rooms).render()
Aspects of the .dat file format
Obstacles
Student question for the day: my room is not a perfect rectangle, it has
an extra column jutting out on one of the walls, what do I do?
Answer: in HW4 such a thing might be omitted, but you might get around
to including the column as an obstacle (a virtual Box) in your .dat file.
The obstacles section is also where things like bookshelves and tables
might go.
obstacles [
Box { # column
Wall {coords [34.3,0,0.2, 34.3,3.05,0.2, 34.3,3.05,0.6, 34.3,0,0.6]}
Wall {coords [34.3,0,0.6, 34.0,0,0.6, 34.0,3.05,0.6, 34.3,3.05,0.6]}
Wall {coords [34.0,0,0.6, 34.0,3.05,0.6, 34.0,3.05,0.2, 34.0,0,0.2]}
}
]
Ceilings?
Originally there was no special ceiling syntax; before now a
person had to put a decoration up there in order to change the ceiling.
However, ceilings are very much like floors, so I went into model.icn
procedure parseroom() and added the following after the floor code.
Hopefully it is there now in the public version.
The parser is
a handwritten in a vaguely recursive descent style; at syntax levels where
many fields can be read, it builds a table (so order does not matter)
from which we populate an object's fields. So the table t's fields correspond
to what the file had in it, and the .coords here is the list of vertices
which the object in the model wants.
if \ (t["ceiling"]) then {
t["ceiling"].coords := [t["x"],t["y"]+t["h"],t["z"],
t["x"],t["y"]+t["h"],t["z"]+t["l"],
t["x"]+t["w"],t["y"]+t["h"],t["z"]+t["l"],
t["x"]+t["w"],t["y"]+t["h"],t["z"]]
t["ceiling"].set_plane()
r.ceiling := t["ceiling"]
}
This allows us to say declarations like this in .dat files:
ceiling Wall {
texture jeb230calendar.gif
}
I have updated jeb1.zip to include these changes on the website.
Event Handling
Most programs that open a window or use a graphical user interface
are event-driven meaning that the program's main job is to
sit around listening for user key and mouse clicks, interpreting them
as instructions, and carrying them out. Pending() returns a list of
events waiting to be processed. Event() actually returns the key or
mouse event. For a simple demo program, one could code the event
processing loop oneself, something like the following.
repeat {
if *Pending() = 0 then { # continue in current direction, if any }
else {
case ev := Event() of {
Key_Up: cam_move(xdelta := 0.05) # Move Foward
... other keys
}
}
Events may be strings (for keyboard characters), but
most are small negative integer codes, with symbolic names such as
Key_Up defined in keysyms.icn.
$include "keysyms.icn"
The Jeb1 demo isn't this simple, since it embeds the 3D window in a Unicon GUI interface. Events will be discussed in more detail below; for now it is enough
to say that they just modify the camera location and tell the
scene to redraw itself. cam_move() checks for a collision and if not, it
updates the global variables (e.g. posx,posy,posz). After the cam_move(),
function Eye(x,y,z,lx,ly,lz) sets the camera position and look direction.
jeb1 is a Unicon GUI application. The GUI owns the control flow and calls
a procedure when an interesting event happens. In Unicon terminology, a
Dispatcher runs the following loop until the program exits. The key call
is select(), which tells is which input sources have an event(s) for us.
method message_loop(r)
local L, dialogwins, x
connections := []
dialogwins := set()
every insert(dialogwins, (!dialogs).win)
every put(connections, !dialogwins | !subwins | !nets)
while \r.is_open do {
if x := select(connections,1)[1] then {
if member(subwins, x) then {
&window := x
do_cve_event()
}
else if member(dialogwins, x) then do_event()
else if member(nets, x) then do_net(x)
else write("unknown selector ", image(x))
# do at least one step per select() for smoother animation
do_nullstep()
}
else do_validate() | do_ticker() | do_nullstep() | delay(idle_sleep)
}
end
do_event() calls the normal Unicon GUI callbacks for the menus, textboxes,
etc. do_cve_event() is a GUI handler for keys in the 3D subwindow.
method do_cve_event()
local ev, dor, dist, closest_door, closest_dist, L := Pending()
case ev := Event() of {
Key_Up: {
xdelta := 0.05
while L[1]===Key_Up_Release & L[4]===Key_Up do {
Event(); Event(); xdelta +:= 0.05
}
cam_move(xdelta) # Move Foward
}
Key_Down: {
xdelta := -0.05
while L[1]===Key_Down_Release & L[4]===Key_Down do {
Event(); Event(); xdelta -:= 0.05
}
cam_move(xdelta) # Move Backward
}
Key_Left: {
ydelta := -0.05
while L[1]===Key_Left_Release & L[4]===Key_Left do {
Event(); Event(); ydelta -:= 0.05
}
cam_orient_yaxis(ydelta) # Turn Left
}
Key_Right: {
ydelta := 0.05
while L[1]=== Key_Right_Release & L[4] === Key_Right do {
Event(); Event(); ydelta +:= 0.05
}
cam_orient_yaxis(ydelta) # Turn_Right
}
"w": looky +:= (lookdelta := 0.05) #Look Up
"s": looky +:= (lookdelta := -0.05) #Look Down
"q": exit(0)
"d": {
closest_door := &null
closest_dist := &null
every (dor := !(world.curr_room.exits)) do {
if not find("Door", type(dor)) then next
dist := sqrt((posx-dor.x)^2+(posz-dor.z)^2)
if /closest_door | (dist < closest_dist) then {
closest_door := dor; closest_dist := dist
}
}
if \closest_door then {
if \ (closest_door.delt) === 0 then {
closest_door.start_opening()
}
else closest_door.done_opening()
closest_door.delta()
}
}
-166 | -168 | (-(Key_Up|Key_Down) - 128) : xdelta := 0
-165 | -167 | (-(Key_Left|Key_Right) - 128) : ydelta := 0
-215 | -211 : lookdelta := 0
}
Eye(posx,posy,posz,lookx,looky,lookz)
end
The Line Between jeb1.icn and model.icn
This program is a rapid prototype to test a concept. Originally it
was a single file (a single procedure!), but after the initial
demo (by Korrey Jacobs, adapted by Ray Lara, both at NMSU)
proved the concept, Dr. J started reorganizing it into two categories,
the code providing the underlying modeling capabilities (model.icn)
and the code providing the user interface (jeb1.icn). The
dividing line is imperfect, we might want to move some code from
one file into the other.
model.icn
We may as well start with the larger of the two source files.
model.icn is intended to be usable for any cve, not just the UI
CS department CVE. It defines classes Door, Wall, Box, and Room,
where Room is a subclass of Box.
Wall() is the simplest class here, it is just a textured polygon,
holding a texture value and a list of x,y,z coordinates, and
providing a method render(). Every object in the CVE's model
will provide a method render().
class Wall(texture, coords)
method render()
if current_texture ~=== texture then {
WAttrib("texture="||texture, "texcoord=0,0,0,1,1,1,1,0")
current_texture := texture
}
(FillPolygon ! coords) | write("FillPolygon fails")
end
initially(t, c[])
texture := t
coords := c
end
Class Box() is more interesting, it is a rectangular area with walls
that one cannot walk through, and a bounding box for collision detection.
Doors, openings, and other exceptions are special-cased by subclassing
and overriding default behavior. Rectangular areas are singled out
because they are common and have easy collision detection; when a wall
goes from floor to ceiling, collision detection reduces to a 2D
problem.
Box() has methods:
- mkwall() (to make walls)
- render() (to draw)
- disallows(x,z) to assist in collision detection
Class Door() is not just a graphical object, it is a connection between (2)
rooms, which can be open (1.0) or closed(0.0) or in between. It supports
methods:
- render() to draw the door
- set_openness(o) to specify how open (ajar) the door is
- add_room(r) to attach a room to the door
- other(room) to compute the other room
- allows(px,pz) to check whether the door is open enough to walk through.
- start_opening(), delta(), and done_opening() when the user activates
the door, as the door is opening or closing, and when it finishes.
The direction toggles each time, between opening and closing.
The full code of jeb1.icn
import gui
$include "guih.icn"
class Untitled : Dialog(chat_input, chat_output, text_field_1, subwin)
method component_setup()
self.setup()
end
method end_dialog()
end
method init_dialog()
end
method on_exit(ev)
write("goodbye")
exit(0)
end
method on_br(ev)
end
method on_kp(ev)
end
method on_mr(ev)
end
method on_subwin(ev)
write("subwin")
end
method on_about(ev)
local sav
sav := &window
&window := &null
Notice("jeb1 - a 3D demo by Jeffery")
&window := sav
end
method on_chat(ev)
chat_output.set_contents(put(chat_output.get_contents(), chat_input.get_contents()))
chat_output.set_selections([*(chat_output.get_contents())])
chat_input.set_contents("")
end
method setup()
local exit_menu_item, image_1, menu_1, menu_2, menu_bar_1, overlay_item_1, overlay_set_1, text_menu_item_2
self.set_attribs("size=800,750", "bg=light gray", "label=jeb1 demo")
menu_bar_1 := MenuBar()
menu_bar_1.set_pos("0", "0")
menu_bar_1.set_attribs("bg=very light green", "font=serif,bold,16")
menu_1 := Menu()
menu_1.set_label("File")
exit_menu_item := TextMenuItem()
exit_menu_item.set_label("Exit")
exit_menu_item.connect(self, "on_exit", ACTION_EVENT)
menu_1.add(exit_menu_item)
menu_bar_1.add(menu_1)
menu_2 := Menu()
menu_2.set_label("Help")
text_menu_item_2 := TextMenuItem()
text_menu_item_2.set_label("About")
text_menu_item_2.connect(self, "on_about", ACTION_EVENT)
menu_2.add(text_menu_item_2)
menu_bar_1.add(menu_2)
self.add(menu_bar_1)
overlay_set_1 := OverlaySet()
overlay_set_1.set_pos(6, 192)
overlay_set_1.set_size(780, 558)
overlay_item_1 := OverlayItem()
overlay_set_1.add(overlay_item_1)
overlay_set_1.set_which_one(overlay_item_1)
self.add(overlay_set_1)
subwin := Subwindow3D()
subwin.set_pos(14, 195)
subwin.set_size("767", "551")
subwin.connect(self, "on_subwin", ACTION_EVENT)
subwin.connect(self, "on_br", BUTTON_RELEASE_EVENT)
subwin.connect(self, "on_mr", MOUSE_RELEASE_EVENT)
subwin.connect(self, "on_kp", KEY_PRESS_EVENT)
self.add(subwin)
chat_input := TextField()
chat_input.set_pos("12", "162")
chat_input.set_size("769", "25")
chat_input.set_draw_border()
chat_input.set_attribs("bg=very light green")
chat_input.connect(self, "on_chat", ACTION_EVENT)
chat_input.set_contents("")
self.add(chat_input)
chat_output := TextList()
chat_output.set_pos("10", "29")
chat_output.set_size("669", "127")
chat_output.set_draw_border()
chat_output.set_attribs("bg=very pale whitish yellow")
chat_output.set_contents([""])
self.add(chat_output)
image_1 := Image()
image_1.set_pos("686", "31")
image_1.set_size("106", "120")
image_1.set_filename("nmsulogo.gif")
image_1.set_internal_alignment("c", "c")
image_1.set_scale_up()
self.add(image_1)
end
initially
self.Dialog.initially()
end
#
# N3Dispatcher is a custom dispatcher. Currently it knows about 3D
# subwindows but we will extend it for networked 3D applications.
#
class N3Dispatcher : Dispatcher(subwins, nets, connections)
method add_subwin(sw)
insert(subwins, sw)
end
method do_net(x)
write("do net ", image(x))
end
method do_nullstep()
local moved, dor
thistimeofday := gettimeofday()
thistimeofday := thistimeofday.sec * 1000 + thistimeofday.usec / 1000
if (delta := thistimeofday - \lasttimeofday) < 17 then {
delay(17 - delta)
}
lasttimeofday := thistimeofday
if xdelta ~= 0 then {
cam_move(xdelta)
moved := 1
}
if ydelta ~= 0 then {
cam_orient_yaxis(ydelta)
moved := 1
}
if lookdelta ~= 0 then {
looky +:= lookdelta; moved := 1
}
every (\((dor := !(world.curr_room.exits)).delt)) ~=== 0 do {
if dor.delta() then moved := 1
else dor.done_opening()
}
if \moved then {
Eye(posx,posy,posz,lookx,looky,lookz)
return
}
end
method cam_move(dir)
local deltax := dir * cam_lx, deltaz := dir * cam_lz
if world.curr_room.disallows(posx+deltax,posz+deltaz) then {
deltax := 0
if world.curr_room.disallows(posx+deltax,posz+deltaz) then {
deltaz := 0; deltax := dir*cam_lx
if world.curr_room.disallows(posx+deltax,posz+deltaz) then {
fail
}
}
}
#calculate new position
posx +:= deltax
posz +:= deltaz
#update look at spot
lookx := posx + cam_lx
lookz := posz + cam_lz
end
#
# Orient the camera
#
method cam_orient_yaxis(turn)
#update camera angle
cam_angle +:= turn
if abs(cam_angle) > 2 * &pi then
cam_angle := 0.0
cam_lx := sin(cam_angle)
cam_lz := -cos(cam_angle)
lookx := posx + cam_lx
lookz := posz + cam_lz
end
global lasttimeofday
#
# Execute one event worth of motion and update the camera
#
method do_cve_event()
local ev, dor, dist, closest_door, closest_dist, L := Pending()
case ev := Event() of {
Key_Up: {
xdelta := 0.05
while L[1]===Key_Up_Release & L[4]===Key_Up do {
Event(); Event(); xdelta +:= 0.05
}
cam_move(xdelta) # Move Foward
}
Key_Down: {
xdelta := -0.05
while L[1]===Key_Down_Release & L[4]===Key_Down do {
Event(); Event(); xdelta -:= 0.05
}
cam_move(xdelta) # Move Backward
}
Key_Left: {
ydelta := -0.05
while L[1]===Key_Left_Release & L[4]===Key_Left do {
Event(); Event(); ydelta -:= 0.05
}
cam_orient_yaxis(ydelta) # Turn Left
}
Key_Right: {
ydelta := 0.05
while L[1]=== Key_Right_Release & L[4] === Key_Right do {
Event(); Event(); ydelta +:= 0.05
}
cam_orient_yaxis(ydelta) # Turn_Right
}
Key_PgUp |
"w": looky +:= (lookdelta := 0.05) #Look Up
Key_PgDn |
"s": looky +:= (lookdelta := -0.05) #Look Down
"q": exit(0)
"d": {
closest_door := &null
closest_dist := &null
every (dor := !(world.curr_room.exits)) do {
if not find("Door", type(dor)) then next
dist := sqrt((posx-dor.x)^2+(posz-dor.z)^2)
if /closest_door | (dist < closest_dist) then {
closest_door := dor; closest_dist := dist
}
}
if \closest_door then {
if \ (closest_door.delt) === 0 then {
closest_door.start_opening()
}
else closest_door.done_opening()
closest_door.delta()
}
}
-166 | -168 | (-(Key_Up|Key_Down) - 128) : xdelta := 0
-165 | -167 | (-(Key_Left|Key_Right) - 128) : ydelta := 0
-215 | -211 | (-(Key_PgUp|Key_PgDn)-128): lookdelta := 0
}
Eye(posx,posy,posz,lookx,looky,lookz)
end
method message_loop(r)
local L, dialogwins, x
connections := []
dialogwins := set()
every insert(dialogwins, (!dialogs).win)
every put(connections, !dialogwins | !subwins | !nets)
while \r.is_open do {
if x := select(connections,1)[1] then {
if member(subwins, x) then {
&window := x
do_cve_event()
}
else if member(dialogwins, x) then do_event()
else if member(nets, x) then do_net(x)
else write("unknown selector ", image(x))
# do at least one step per select() for smoother animation
do_nullstep()
}
else do_validate() | do_ticker() | do_nullstep() | delay(idle_sleep)
}
end
initially
subwins := set()
nets := set()
dialogs := set()
tickers := set()
idle_sleep_min := 10
idle_sleep_max := 50
compute_idle_sleep()
end
class Subwindow3D : Component ()
method resize()
compute_absolutes()
# WAttrib(cwin, "size="||w||","||h)
end
method display()
initial please(cwin)
Refresh(cwin)
end
method init()
if /self.parent then
fatal("incorrect ancestry (parent null)")
self.parent_dialog := self.parent.get_parent_dialog_reference()
self.cwin := (Clone ! ([self.parent.get_cwin_reference(), "gl",
"size="||w_spec||","||h_spec,
"pos=14,195", "inputmask=mck"] |||
self.attribs)) | stop("can't open 3D win")
self.cbwin := (Clone ! ([self.parent.get_cbwin_reference(), "gl",
"size="||w_spec||","||h_spec,
"pos=14,195"] |||
self.attribs))
set_accepts_focus()
dispatcher.add_subwin(self.cwin)
end
end
# link "world"
global modelfile
procedure main(argv)
local d
modelfile := argv[1] | stop("uasge: jeb1 modelfile")
world := FakeWorld()
#
# overwrite the system dispatcher with one that knows about subwindows
#
gui::dispatcher := N3Dispatcher()
d := Untitled()
d.show_modal()
end
link model
global world
procedure make_model(cooridoor)
local fin, s, r
fin := open(modelfile) | stop("can't open ", image(modelfile))
while s := readlin(fin) do s ? {
if ="#" then next
else if ="Room" then {
r := parseroom(s, fin)
put(world.Rooms, r)
world.RoomsTable[r.name] := r
if /posx then {
r.calc_boundbox()
posx := (r.minx + r.maxx) / 2
posy := r.miny + 1.9
posz := (r.minz + r.maxz) / 2
lookx := posx; looky := posy -0.15; lookz := 0.0
}
}
else if ="Door" then parsedoor(s,fin)
else if ="Opening" then parseopening(s,fin)
# else write("didn't know what to do with ", image(s))
}
close(fin)
end
#CONTROLS:
#up arrow - move foward
#down arrow - move backward
#left arrow - rotate camera left
#right arrow - rotate camera right
# ' w ' key - look up
# ' s ' key - look down
# ' d ' key - toggle door open/closed
#if you get lost in space (may happen once in a while)
#just restart the program
$include "keysyms.icn"
#GLOBAL variables
global posx, posy, posz # current eye x,y,z position
global lookx, looky, lookz # current look x position and so on
global cam_lx, cam_lz, cam_angle # eye angles for orientation
global xdelta, ydelta, lookdelta
global Rooms
procedure please(d)
local r
&window := d
WAttrib("texmode=on")
#initialize globals
# posx := 32.0; posy := 1.9; posz := 2.0
# lookx := 32.0; lookz := 0.0; looky := 1.75
cam_lx := cam_angle := 0.0; cam_lz := -1.0
# render graphics
make_model()
every r := !world.Rooms do {
if not r.disallows(posx, posz) then
world.curr_room := r
}
every (!world.Rooms).render(world)
xdelta := ydelta := lookdelta := 0
dispatcher.cam_move(0.01)
Eye(posx,posy,posz,lookx,looky,lookz)
# ready for event processing loop
end
# fakeworld - minimal nsh-world.icn substitute for demo
record fakeconnection(connID)
class FakeWorld(
current_texture, d_wall_tex, connection, curr_room,
d_ceil_tex, d_floor_tex, collide, Rooms, RoomsTable
)
method find_texture(s)
return s
end
initially
Rooms := []
RoomsTable := table()
collide := 0.8
connection := fakeconnection()
d_floor_tex := "floor.gif"
d_wall_tex := "walltest.gif"
d_ceil_tex := d_wall_tex
end
### Ivib-v2 layout ##
#...blah blah machine-generated comments omitted...
model.icn
- Model.icn is ~1400 lines
- We can't go through it all, but we can skim it for ideas.
- A small class library for virtual objects.
- Consider logical vs. physical modeling again
- Graphics-only: model should just be a data structure
- Typical 3D artist: model=data structure (with optional animations)
- VE builder: model=code class. unlimited behavior
- But we really want model=data structure!
- Want our cake and eat it too.
model.icn
The only part we had time to look at today was the top of class Room:
Class Room()
Class Room() is the most important, and is presented in its entirety.
From Box we inherit the vertices that bound our rectangular space.
class Room : Box(floor, # "wall" under our feet
ceiling, # "wall" over our heads
obstacles, # list: things that stop movement
decorations, # list: things to look at
exits, # x-y ways to leave room
name
)
A room disallows a move if: (a) outside or (b) something in the way.
The margin of k meters reduces graphical oddities that occur if
the eye gets too near what it is looking at. Note that JEB doors are
kind of narrow, and that OpenGL's graphical clipping makes it relatively
easy to accidentally see through walls.
method disallows(x,z)
if /minx then calc_boundbox()
# regular area is normally OK
if minx+1.2 <= x <= maxx-1.2 & minz+1.2 <= z <= maxz-1.2 then {
every o := !obstacles do
if o.disallows(x,z) then return
fail
}
# outside of regular area OK if an exit allows it
every e := !exits do {
if e.allows(x,z) then {
if minx <= x <= maxx & minz <= z <= maxz then {
# allow but don't change room yet
}
else {
curr_room := e.other(self) # we moved to the other room
}
fail
}
}
return
end
Method render() draws an entire room.
method render()
every ex := !exits do ex.render()
WAttrib("texmode=on")
floor.render()
ceiling.render()
every (!walls).render()
every (!obstacles).render()
every (!decorations).render()
end
The following add_door method tears a hole in a wall. It needs extending to
handle multiple doors in the same wall, and to handle xplane walls. These
and many other features may actually be in model.icn; the code example in
class is a simplified summary.
method add_door(d)
put(exits, d)
d.add_room(self)
# figure out what wall this door is in, and tear a hole in it,
# for example, find the wall the please door is in,
# remove that wall, and replace them with three
every w := !walls do {
c := w.coords
if c[1]=c[4]=c[7]=c[10] then {
if d.x = c[1] then write("door is in xplane wall ", image(w))
}
else if c[3]=c[6]=c[9]=c[12] then {
if abs(d.z - c[3]) < 0.08 then { # door is in a zplane wall
# remove this wall
while walls[1] ~=== w do put(walls,pop(walls))
pop(walls)
# replace it with three segments:
# w = above, w2 = left, and w3 = right of door
w2 := Wall ! ([w.texture] ||| w.coords)
w3 := Wall ! ([w.texture] ||| w.coords)
every i := 1 to *w.coords by 3 do {
w.coords[i+1] <:= d.y+d.height
w2.coords[i+1] >:= d.y+d.height
w2.coords[i] >:= d.x
w3.coords[i+1] >:= d.y+d.height
w3.coords[i] <:= d.x + d.width
}
put(walls, w, w2, w3)
return
}
}
else { write("no plane; giving up"); fail }
}
end
Rooms maintain separate lists for obstacles and decorations.
Obstacles figure in collision detection.
method add_obstacle(o)
put(obstacles, o)
end
method add_decoration(d)
put(decorations, d)
end
Lecture 17. Second Life Session
This session was held in second life. Lecture materials
will be absent from the notes, but please see
http://www2.cs.uidaho.edu/~cs428/hw-secondlife.html
Introduction to Second Life
Second Life (www.secondlife.com) is a social virtual environment. Its
primary feature is user-created content -- Linden Labs did not create this
virtual world, they created a client and server with which end users can
create a virtual world. If the virtual world creation capabilities of
Second Life are sufficiently flexible and powerful, we could presumably
create an MMO inside of it.
Second Life is a non-game virtual environment. It is also a platform
for building different kinds of virtual environment experiences. Many
people are building amazing things in-world, and many are building
amazing interfaces to external resources. Here are some interesting links.
Compared with game MMOs like Lord of the Rings Online or World of Warcraft,
there is nothing to do, and everything
to do. Instead of your character becoming skilled in pretend fishing,
in SL you might yourself become skilled in creating virtual content,
and getting paid for it.
Lecture 18.
Where We are At
- Take-Home Midterms Just Turned In -- will discuss Thursday
- Schedule says: we are a bit behind on the Rabin chapters
- Jeffery says: we are trying to bridge the gap between the JEB demo
and the CVE program, an enormous gulf.
- Need to explore several major topic areas: 3D modeling and animation,
networking for multi-user games, A/I...
- Really want to go beyond the CVE program in some of these areas.
- Time for a new homework assignment,
HW#5.
A "missing link"
Genre-wise, a missing link between the toy-function JEB Demo and the
many-function CVE program, we should be doing a homework that let's
us explore a classic genre: the FPS, or First-Person Shooter
FPS's
This is the genre that built the 3D PC graphics card industry.
There were some 3D games before these, but id Software's early
games really established a genre. Note that they were a tiny team,
without big backing, and released their early hits as shareware in
the 1990's.
- Wolfenstein 3D
- Doom
- Quake (source)
From the Quake3 source code you can tell that one option for us to FPS it in
style is just to learn and understand their 335K LOC engine well enough to
modify it to do whatever we want. Software engineers after all believe
strongly in code re-use and it is spectacularly generous of Id to release
their code.
Another option is to search for higher (-level) ground, such as filling in
some of the gaps between the ~2.5K LOC jeb demo and the FPS genre.
The main differences between wandering around the halls of a CS department
(the JEB demo) and Wolfenstein 3D or Doom are:
- animated 3D enemy characters
- 3D models, simple A/I
- simple combat system
- health meter, attack and damage animations
- a very few virtual objects
- weapons, armor, health kits
The latter two have been addressed in HW#2 in this class. The animated
3D enemy characters will drive our next couple lectures and homework 5.
3D Modeling, Part I
- A 3D Model, for the purposes of this class
- A data structure (with an external representation -- a file format)
capable of representing an arbitrary-shaped, generally solid object
and that object's range of motions.
- Start with: Polygon Mesh and Texture(s)
- Add: bone structure, motion API
- (strangely?) OpenGL does not have built-in 3D models.
- If you dig, you find that SGI did a sweet toolkit atop OpenGL
called OpenInventor, but that never became ubiquitous as OpenGL.
OpenInventor influenced the design of VRML, which influenced the
design of X3D.
- No universal standard for 3D modeling that I know of.
- Suggestions welcome
- 3D Model File Format Requirements
- "open". public. platform neutral. easily parsed.
preferably human readable. "adequate" performance.
- scripted animation versus program control?
- Sort of a question of how much manual artistry can we afford.
Artists make better-looking animation, but are too expensive.
Ideal would be: a set of general parametric or programmable animations.
- In this class we will discuss two 3D model formats: S3D and .X
- The S3D (simple 3D)
Check out s3dparse.icn
You really need to see some
sample S3D files
in order to get a feel for the beauty of the S3D file format.
s3dparse.icn, and S3D files themselves, may have
various usually-nonfatal "bugs". There was even a
bug in the S3D file format document.
We also took a look at the desperate situation vis a vis creating
3D models (a job performed by experts with much training)
and our need to build such models for our games and virtual environments.
Let us continue from there.
A Grim S3D Example (Part 1)
Consider the following texture images of a character (you can pretend
it is a space marine or pirate to shoot at, if you want).
- What is the absolute minimum number of polygons in order for
produce a 3D solid based on these textures?
-
How many polygons -- how much of a mesh -- do we have to have in order to
generate a "reasonable" 3D model?
- What is the maximum*?
Dr. J is 5'9" (1.75m), his elbow-elbow width is approximately 24" (0.61m) and
his front-back is 11" (.28m) at the belly.
(*this is a trick question)
Dr. J Model 0 - Stuck in a PhoneBooth, Hardwired Code
To get someone visible within Jeb 1, you could add something like
the following right after the rooms are rendered:
drawavatar( ? (world.Rooms) )
This invokes some hardwired code to render an avatar in a randomly
selected room. The procedure to render the digital photos as is,
prior to any 3D modeling, might look like:
procedure drawavatar(r)
# place randomly in room r
myx := r.minx + ?(r.maxx - r.minx)
myy := r.miny
myz := r.minz + ?(r.maxz + r.minz)
# ensure a meter of room to work with
myx <:= r.minx + 1.0; myx >:= r.maxx - 1.0
myz <:= r.minz + 1.0; myz >:= r.maxz - 1.0
PushMatrix()
Translate(myx, myy, myz)
WAttrib("texmode=on","texcoord=0,0,0,1,1,1,1,0")
Texture("jeffery-front.gif")
FillPolygon(0,0,0, 0,1.75,0, .61,1.75,0, .61,0,0)
Texture("jeffery-rear.gif")
FillPolygon(0,0,.28, 0,1.75,.28, .61,1.75,.28, .61,0,.28)
Texture("jeffery-left.gif")
FillPolygon(0,0,0, 0,1.75,0, 0,1.75,.28, 0,0,.28)
Texture("jeffery-right.gif")
FillPolygon(.61,0,.28, .61,1.75,.28, .61,1.75,0, .61,0,0)
PopMatrix()
end
Dr. J Model 0.1 - Stuck in a PhoneBooth, S3D File
The above hardwired code calls FillPolygon four times to draw four rectangles.
If we draw the exact same picture with triangles...we need 8 triangles,
composed from 8 vertices. Taking a wild stab, try the following .s3d file.
I did not get it right on the first try (but I was close). Note that
vertex coordinates (numbers like 1.75, .61, .28) are given in meters
based directly on measurements given earlier.
// version
103
// numTextures,numTris,numVerts,numParts,1,numLights,numCameras
4,8,8,1,1,0,0
// partList: firstVert,numVerts,firstTri,numTris,"name"
0,8,0,8,"drj"
// texture list: name
jeffery-front.gif
jeffery-rear.gif
jeffery-right.gif
jeffery-left.gif
// triList: materialIndex,vertices(index, texX, texY)
0, 0,0,256, 1,0,0, 2,256,0
0, 0,0,256, 2,256,0, 3,256,256
1, 4,0,256, 5,0,0, 6,256,0
1, 4,0,256, 6,256,0, 7,256,256
2, 7,0,256, 6,0,0, 1,256,0
2, 7,0,256, 1,256,0, 0,256,256
3, 3,0,256, 2,0,0, 5,256,0
3, 3,0,256, 5,256,0, 4,256,256
// vertList: x,y,z
0,0,0
0,1.75,0
.61,1.75,0
.61,0,0
.61,0,.28
.61,1.75,.28
0,1.75,.28
0,0,.28
// lightList: "name", type, x,y,z, r,g,b, (type-specific info)
// cameraList: "name", x,y,z, p,b,h, fov(rad)
S3D Rendering, Version 0
Here is naive code to achieve the S3D drawing.
Design note #1: parsing and rendering constitute enough behavior to go
ahead and make a class (or maybe a built-in) out of this. However, Jafar
has written far more sophisticated code we will prefer to use.
Design note #2: while we can make a generic S3D renderer fairly easily,
to animate body parts (legs, arms, etc), our model will need to insert
Rotation capabilities at key articulation points. We will consider this
and the S3D part mechanism after we get "out of the box" into a higher
polygon count.
Performance note: in "real life" there are polygon "mesh modes" that would
allow several/many triangles in a single call. This is the kind of thing
that using Jafar's classes would give you, over doing it yourself.
Note that at one time I began
planning a u3d file format as a minor simplification
based on s3d.
procedure draws3d(r)
loads3d("drj.s3d")
# place somewhere in room r
myx := r.minx + ?(r.maxx - r.minx)
myy := r.miny
yz := r.minz + ?(r.maxz + r.minz)
# ensure a meter of room to work with
myx <:= r.minx + 1.0
myx >:= r.maxx - 1.0
myz <:= r.minz + 1.0
myz >:= r.maxz - 1.0
PushMatrix()
Translate(myx, myy, myz)
WAttrib("texmode=on")
every i := 1 to triCount do {
tri := triangleRecs[i]
v1 := vertexRecs[tri.vi1 + 1]
v2 := vertexRecs[tri.vi2 + 1]
v3 := vertexRecs[tri.vi3 + 1]
Texture(textureRecs[tri.textureIndex + 1]) |
stop("can't set texture ",
textureRecs[tri.textureIndex + 1])
WAttrib("texcoord=" || utexcoord(tri.u1,tri.v1) ||
"," || utexcoord(tri.u2,tri.v2) ||
"," || utexcoord(tri.u3,tri.v3))
FillPolygon(v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v3.x, v3.y, v3.z)
}
PopMatrix()
end
Getting Rid of the Telephone Booth
Jeffery is tired of living inside a phonebooth. It is even more confining
than when he is a Lego-Man. For the next step, what he needs is a way to
specify a bunch of vertices fast, in a manner conducive to S3D files.
Furthermore, if he clicks the "same" vertex in both a front/rear texture
and a left/right texture, he'll be able to (a) acquire (x,y,z) coordinates
for that vertex, and (b) acquire (u,v) coordinates for that vertex to use
for both front/rear-facing AND side-facing triangles. This is still primitive,
but there is some hope that we can build a simple tool
for it.
Chad and Eric Say ...
The ideas in this section are borrowed from "Game Modeling Using
Low Polygon Techniques", by Chad and Eric Walker, Charles River
Media Press, 2001.
- 1. You should start by learning to draw, and draw detailed sketches
of front, side(s), and rear of your character.
- Since we aren't artists, I will settle for digital photos. But you
could substitute a drawing of yourself if you preferred.
- 2. You should draw a polygon outline (profile) from the side view.
The number of polygons might initially be low (20-30?). Each is an
(x,y).
- 3. You extrude yourself by taking each (x,y) and making a right and left
side (x,y,z) from it. z's can be taken my measuring your front view
width, or measuring yourself in RL.
- 4. You map textures
- 5. You refine iteratively by editing vertices and adding surfaces.
.X File Format
- Invented by Microsoft
- Both binary/compressed and human-readable XML-based formats.
- In Unicon, uni/3d/ provides class libraries (4.8K LOC, by Jafar)
- uni/3d/viewer provides a .x (subset) viewer
- uni/3d/models includes a sample model.
Pointers from Jafar:
Thoughts on a 3D Pirate Ship
- This particular example gives us some food for thought looking forward.
- Obtained as a free download from TurboSquid; view license
- might not allow us to sell anything using it
- 864 polygons, Microsoft .x format
- Almost can load as-is with Unicon .x loader, not quite (quads)
- Want to be comfortable using industry-standard format / data structure
in our own games and virtual environments
- want to connect our code to it in meaningful ways.
-
Pirate ship.x -- 12K lines of modelly goodness.
- Generating these things is beyond the scope of this class.
- Using these things is not.
- Pirate Ship textures:
- ~15 different textures rolled into a single (1024x1024) .png file.
Next let's see what the Rabin chapter on Character Animation has to say about
3D Modeling.
Chapter 5.2, Character Animation (Chapter 5.2, original).
(lecture covered slides 1-14).
Lecture 19.
Discussion of Midterm Examination
Lecture 20.
Where we are at
- Schedule says we should have an A/I unit this week;
we introduced some topics from it earlier
- Lecture notes say we should talk about networking and multi-user games
- Gut feeling says we have a lot more to say about 3D
modeling and character animation.
- Extended discussion of midterm last session == HW5 extension
One week: if you need more than that you are doing too much.
Representation: prototype w/ single (texture-wrapped) primitive
for avatars, then refine
- Plan: this week we will talk about A/I and use any time that remains to
work on Avatars and/or character animation.
- Rationale: A/I is vital in almost all games and may contribute to HW5.
- We will have one more "Virtual Environment Experiences" assignment
(last one was SecondLife), and one more programming assignment
(Semester Project) after the current FPS assignment (due in one week?)
- In Moscow: Dr. Soule's seminar after class today sounds real interesting
Minecraft Demo/Discussion
Dr. J's main question is: to what extent is Minecraft really a
a persistent multi-user virtual world?
Another .X model Example
u3dview:
from:
warrior.x and warrior.gif
Rabin of the Day
Chapter 5.3, AI Agents.
(backup Chapter 5.3, AI Agents)
This is an interesting chapter that we may spend a whole
class on, and could spend longer.
Lecture 21.
Discussion of u3dviewer
Unicon's uni/3d/viewer application shows how to pull a 3d model from a .x
file and render it within a 3D application such as the JEB demo.
Reflections on "A/I week"
- How much sensing, thinking, and acting can be done in HW5?
- Sensing:
- level 0=mob is a rock, no response
- level 1=tactile (senses attacks, sometimes their sources)
- level 2=hearing/smell (senses near-proximity)
- level 3=seeing (senses farther off)
- physical model? or just saving throws + modifications?
- character could have base obtrusiveness value based on size/class
- modifications for distance, other sights/sounds, monster alertness
- Thinking:
- assess attitude towards (cause of) sensed event, e.g. player
- models include: hardwired (kill-on-sight), alignment, to race/faction
- how much would this monster really know about player X?
- assess relative strength of self vs. opponent (fight or flight?)
- what stats are directly observable?
- what stats might a monster infer, or guess, and from what?
- select a response plan
- basic plans
- how often should plans be revised or replaced?
- Acting:
- movement (towards, away)
- attack or defend
- communicate (w/ neighbors or opponents)
Rabin: Character Animation (cont'd)
- What are the important parts of
Chapter 5.2, Character Animation (Chapter 5.2, original).
Lecture 22
This week:
- multi-user gaming, particularly networking,
including Rabin chapter 5.6
- cover chapter 5.5 (Audio) later
- the CVE virtual environment and its
implementation
- maybe Rabin 4.2 (collision detection) on Thursday.
Multi-user Games
Beyond two players sharing a keyboard or a couple of joysticks, most
multi-user games employ network communication in order for players
to interact in the game. Let's pre-test your network programming knowledge
against the following buzzwords:
- TCP, IP, and UDP
- (internet) ports
- packets and streams
- latency and bandwidth
- clients and servers
- synchronous and asynchronous
While this course isn't about networks, games use them and it is
appropriate to provide a brief introduction to network programming.
Especially if you have never done network programming before,
you should read Chapters 5 and 15 of the
Unicon
book for a discussion of network programming in Unicon. For other
languages you wish to use, you should seek out (and try out) their
comparable functionality.
The main networking buzzword: Protocol
Q: So, what is a network protocol?
A: A network protocol is the "language" by which two programs agree to
communicate over the network. The format of each individual message
is analogous to a file format, but it is also analogous to a set of
lexical and syntax rules like those used in compilers. The sequence
of messages that are allowed are analogous to syntax and semantic
rules in compilers.
Two major families of protocols
- They are: stream-oriented and packet-oriented.
- Packet-oriented protocols might specify fixed, constant-sized packets.
Examples are usually low-level (ATM switch, ethernet, WSN...)
- Many protocols including the internet protocols
IP, TCP, and UDP are packet-oriented and specify
the exact binary layout of data within fixed-size headers, followed by
variable-length data payloads which can be either binary or textual.
- For example, IP specifies 20 byte headers.
- Packet-oriented protocols tend to be "efficient" but "non-portable";
the network delivers C struct data intact.
- Packet-oriented protocols
are more effective
when working on a homogeneous set of machines (all the same CPU, etc.).
- Software that has to run across
machines with different byte ordering or CPU wordlengths benefit less
from, and have to be careful of bugs when dealing with such binary data.
- Differences in compiler and operating system may also result in
differences for protocols that have to run portably, especially for
packet-oriented protocols.
Stream-oriented protocols are usually more human-readable, with ASCII text
line-oriented message formats. For example, HTTP protocol sends its
headers as a sequence of lines with a easily readable format like:
Fieldname: value
Fieldname2: value2
... ending with a blank line, after which the data payload follows.
Rabin
-
Chapter 5.6
(5.6, backup).
-
Lecture 23
Network programming examples from the Unicon Book
Review chapter 5. Be able to describe open() modes
"n", "na", "nl", "nu", "nua". read about the select() function.
Connecting in Unicon
Unicon supports TCP and UDP internet protocols, which are the building
blocks for most higher level application protocols such as those that provide
mail, news, www, etc. We will mainly consider TCP in this lecture. UDP is
also available, and has desirable properties for a CVE; we might or might
not talk about it later in the semester.
A client requests an outgoing connection to server with
f := open("machine:port", "n")
where machine may be an IP number or a domain name, and port is a
nonnegative integer. The call will fail if no server is present,
although it may wait awhile before giving up, since servers may be
slow or the network may be congested.
Once the connection is open, the program has a file opened for reading and
writing. Care must be taken to design communications so that no deadlock
can occur where both sides are waiting for the other. In many client-
server applications there is a strict request-response format to the
communications.
Receiving a Connection in Unicon
Testing network applications can be a pain, you either must connect to
an existing server using its known protocol, or you have to somehow run
a server program yourself just to test your client. The machine name
"localhost" often works to refer to your own machine regardless of its
other name(s); another popular name is 127.0.0.1. In any case, unless
you are root, you will have to use some large port number to run any
server, such as 4500; perhaps any number between 1000 and 8000 or 16000
would be OK, as long as you are using a port no other application is
using. Sometimes during debugging, it may take awhile for a crashed
server process to give up a port it was on, so you may have to rotate
through a few port numbers before you find one you can open.
In any case, a server requests an incoming connection from a client
with
f := open(":port", "na")
or
f := open(":port", "nl")
Accept mode "na" will block until a connection is received. Listener mode
"nl" will open an incoming connection partially ("listening") without
waiting for a connection; it must be used with the select() function.
When a listener is passed in to select() as one possible communication
that might happen, and when an incoming connection actually occurs, the
"listener" is converted into a regular opened connection. This is handy
in servers, and enables them to serve multiple callers without having to
use "threads".
select()
Consider the beauty and virtue of select():
while *(L := select(f1,f2,f3,timeout)) = 0 do handle_timeout()
every f := !L do {
... handle a read on one of f1, f2, or f3 where input is available
}
Look at the Examples in Chapter 15 of the
Unicon Book
Addition to the book! From our experiences writing a multiuser
virtual environment, we ended up adding a new function to Unicon
named ready(). ready() is like reads()
except that it is non-blocking.
It returns immediately with available data, rather
than waiting until data arrives
ready() has proven essential in CVE.
Scalability and Multi-user Gaming
Multi-user games have both soft and hard limits on how many users they can
handle. Network programming can be easy, but naive network programming will
result in surprisingly bad limits. For example, during the first year of
my collaborative virtual environment project, the system degraded and failed
after 5 users: things were slightly degraded but usable with 5 users, and
would hang and eventually crash with 6 users. In order to raise the limits,
you have to become aware of several technical limits which interact:
- bandwidth
- this is the "easiest" limit to remember, most of us know our
internet connection can only transmit so many bytes per second,
so transferring big files will take time.
What you need to add to that knowledge
is that during the same connection, bandwidth fluctuates wildly
as other internet traffic varies. Also, across a WAN the bandwidth
is limited by the "weakest link", so on my office machine with its
gigabit network, connectivity to NMSU is only 1.2 megabits/second,
and this factor of 800 slowdown varies from second to second
(from 800X slower than the
computer's NIC can handle to 1600X to infinity).
Compressing files might take many billions of CPU instructions but
still greatly
speed up a large transfer; it is a typical bandwidth-reduction
mechanism.
- latency
- the delay between sending and receiving packets also varies from
very low to very high. Typical across campus latencies might be
50ms (up to 20 times per second? up to 10 round trips per second?),
while latency across the internet is often in the 1.5 second range.
Multiuser games have to be designed to not depend on low latency;
for example, a "heartbeat" to keep all players in sync is a tempting
idea, but if local client redraws waited for such a heartbeat, you
would not get a high enough refresh rate for a smooth animation.
Dead-reckoning is a typical latency-compensation mechanism.
- # of packets
- The bandwidth may be near infinity, the latency may be no problem,
and the network may still impose limits: each packet costs the OS
a lot of processing time to handle, whether it carries 6 bytes or 1.5KB
or more. Packet aggregation is a typical packet reduction mechanism.
- GPU
- A GPU may easily be a limiting factor on the # of users. If graphic
updates/changes are proportional to # of users, or # of polygons
to be displayed is proportional to # of users, adding more users will
gradually break the client's ability to update, starting with low-end
non-GPU computers and working up even to high end machines.
Level-of-detail is a typical means of compensating for limited GPU
resources.
- CPU
- A CPU may easily impose a limit on # of users, CPU's handle
core gameplay and user interaction plenty fast, but games tend to
dump lots of extra work on the CPU, assuming it is an infinite resource.
If you are on a low-end CPU, you may save CPU cycles by NOT doing a lot
of the other limitation-reducing techniques which suck CPU resources.
Switching to more efficient algorithms and data structures, and doing
profiling and performance tuning are other typical CPU-saving techniques.
- Main memory
- Main memory is usually a major bottleneck in modern computing systems.
- OS
- Any time you interact with the operating system will greatly slow your
program down. Whole careers have been built on the art of reducing the number
of OS calls. For example in MS-DOS days the standard thing to do was to skip
the OS and write directly to video memory for fast graphics. In modern UNIX
and Linux systems, processes were too slow so threads were invented, and
OS threads were too slow so "user threads" were invented.
- disk
- A program that spends time waiting for disk I/O may not be able to
sustain game-level refresh rates. Many disk operations can be avoided
by leaving files' contents in main memory, and only writing changed
items out to disk periodically.
How to Handle More Users - discussion of processes and threads
The oldest internet models have a single-process, single-thread server
that receives a request, replies immediately, and awaits the next request.
This was immediately followed by a "fork-exec" model, in which each
incoming connection triggers a new process, so that multiple users can
be served simultaneously. Separate server processes for each user gives
good fault tolerance (one user's server process crashing might not affect
others') and poor/slow communication for applications where users
interact with each other via the server.
Since process creation is slow, "fork-exec" has been replaced by various
newer models, including farming the work out to a pool of pre-created
processes, and using threads instead of processes.
Context switching between processes is very slow, and even switching between
threads is pretty slow. In addition, communication between processes or
even threads is slow. For these reasons, modern multi-user servers might
have each thread handling several user connections -- especially if certain
users tend to communicate together a lot. The number of users per thread
might depend on how CPU-intensive the server threads' tasks are in support
of each user -- if the server has to do a lot of work for each user
transaction, it is easier to justify a separate thread for each user.
Are Virtual Environments only for Large Corporations?
I am not
the only person crazy enough to propose garage-scale MMO development.
Note that without a certain level of 3D graphics
capability we cannot undertake this goal at all, and unless we find a
way to make 3D graphics quite easy, it is far beyond our available resources.
Introduction to OpenWonderland and OpenSim
- Sun's
OpenWonderland is a Java-based "Toolkit for Building 3D Virtual Words".
- OpenSimulator is a "more open"
clone of SecondLife that strips out a lot of unnecessary stuff.
Introduction to CVE
CVE is our homemade research CVE. It lives at cve.sf.net.
CVE has been called Unicron and VIEW in the past, and may get
renamed in the future. I would always like a better name.
- CVE implementation "book"
- http://www2.cs.uidaho.edu/~hani/cve_binary/setup-cve-test.exe
- The best way to get current source code at present is via SVN per
instructions at http://cve.sf.net
(source .zip files exist but need updating)
-
Chapter 4.2, Collision Detection
-
Chapter 5.6
(5.6, backup).
-
Lecture 24.
Homeworks
- HW#5 is due today. If you need more time, my late policy will be just
as lenient as my grading is tardy. Still, you should finish it up ASAP
so you can work on...
- HW#6
- HW-CVE
More on Virtual Environments
CVEs are a preliminary, low-grade form of virtual reality as
envisioned in the 1980's by science fiction authors such as William Gibson,
Neal Stephenson, and others. While these authors and many subsequent
movies have envisioned computer environments indistinguishable from the
physical world, CVEs run on conventional computers and are only as
"immersive" as one's imagination and one's computer monitor allow them to be.
Intermediate forms of virtual reality are made possible by higher end 3D
display devices such as the current crop of 3D TV's, and motion tracking
hardware and software.
Hard Technical Subjects in CVEs
CVEs are potentially amazingly complex pieces of software.
A CVE generally requires sophisticated 3D graphics (hard),
complex peer-to-peer and/or client/server multiuser networking (hard),
and a lot of application domain logic for the type of collaboration
that is to be supported. CVEs may also integrated many other aspects
of CS (such as artificial intelligence) to make the virtual
environment richer and more useful.
Because writing a CVE is potentially so incredibly technically challenging,
there is a danger that the only people who can do it are large
multi-million-dollar industry labs. In this class we are interested in
CVEs as vehicles for both direct and indirect research:
- research results that enhance the value/usefulness of CVEs
(CVEs as a direct research domain; communication/interaction/etc.)
- research results that reduce the difficulty of writing CVEs
(CVEs motivate research in programming languages and systems)
CVEs as Places for Action and Interaction
How do people collaborate in a virtual environment?
- textual chat; talking and listening
- by seeing each other, waving, smiling, etc.
- by following each other to specific locations or shared experiences
- by manipulating objects in the virtual environment. The objects
might correspond to real-world artifacts: files, printers, etc.
- by creating new objects or spaces in the virtual environment.
- by recording an experience for others to view at a later time
- ??? how about some suggestions ???
For CVE's to be useful, their concept of space needs to make sense.
Some rooms may be for specific activities, or specific kinds of work;
others may be shared, or used for different purposes at different times.
Collaborative Work
The "Collaborative" part of a CVE ties it to the field of Computer
Supported Collaborative Work (CSCW). This is a relatively well-established
area of CS, with its own community. We will explore this field to some
extent in this course. Here are some immediate implications:
- A CVE exists for a Purpose; people use it to Do Something.
- CVE's are inferior to real-world collaboration facilities.
Under what circumstances do they make sense?
- A CVE provides a shared context among users. They should
be able to see the same things.
- A CVE supports awareness of others: users should see each other.
- A CVE enables communication between users.
- A CVE might allow multiple, flexible viewpoints.
Shared Context
Seeing what each other is doing; seeing each other's past; shared access
to data; shared space in the 3D environment.
Awareness of others
See not just that other users are there, but what the other users are
doing, especially when it affects or relates to what you are doing.
There is foreground awareness and background awareness. Things should be in
the background unless/until they start interfering with what you are doing.
Background awareness may include users' real locations and schedules,
whether they are at their keyboard and looking at the screen at the moment,
what task they are performing, etc.
Communication
There are several dimensions to the direct communication between computer
users. Communication can be textual, graphical, and audio/video. It can be
in real-time or recorded for later. Things like tone of voice, hand gestures
and eye behavior can significantly affect real conversations; how can they
be approximated in computer-based communication?
CVE's and Entertainment
Games have driven many of the recent advances in computer graphics, and
CVE's are no exception. Videogames like Doom proved that 3D applications
could be highly immersive even without photorealism. MMRPG's such as
Everquest have proven the potential of CVE's far more convincingly than
the research products discussed in our textbook and the CVE conferences.
With a compelling proof-of-feasibility like Everquest in mind, we cannot
help but believe that a CVE will soon dominate many fields of remote
communication and endeavor. It is only a matter of time before CVE's
are used for distance education, virtual dating and sex, live theater,
circus and other public performances, as well as major meetings such
as conferences, associations, and the activities of governmental organizations.
lecture#25
Attention Students!
Mike Griffin, the former Administrator of NASA (appointed by President
Bush), will be on campus on Monday, April 18. He will be meeting
exclusively with students at 10AM in the Commons Whitewater room.
If you are interested in attending the Student visit at 10AM, please let
me or Becky Highfill know (bhighfil@uidaho.edu). There are a limited
number of seats available and its very important that we know how many
students are planning to attend. If you have any interest in NASA, space
exploration, aerospace engineering, or space systems and technologies,
this will be a very interesting meeting.
Thank you,
Brittany Harding
Communications Assistant
nasacomm@uidaho.edu
Audio in more HWs
OK, so what will it take for us to get some more audio into our final
projects? What kinds of audio would be useful in your project? Where
can you get it?
I would think it might include:
- audio effects for combat
- alerts marking change of state in the game
(ship sighted, land ho, arrival in port etc)
- introduction and winning/losing
- theme music for major zones (soundscape?)
Rabin
A portion of today's talk is from a
GamaSutra article, please read it.
Content Creation
There are two types of user-created content as envisionable in current
and future virtual environments. You can think about this from the point
of view of the game author (e.g. Blizzard, or you doing your HW) or from
the inexorable web 2.0 point of view: interactive, end-user creation.
- visual/graphical content
- composed from 3D primitives, includes both static and dynamic/behavioral
content. Our conspicuous examples are SecondLife and Minecraft.
- things to do in-game.
- part of this is game design/mechanics/coding. When is it content creation?
We mentioned a few examples of this previously, such as City of Heroes'
architect facilities.
I'd like to think about both.
One of the reasons to study content creation is to test its limitations and
see what ideas ought to be present in future virtual worlds we might build.
Creating 3D Primitives
I am stating the next few paragraphs in terms of SecondLife, but Minecrafties,
please feel free to point out any contrasts that we haven't already observed.
In SL, you can only build where either (a) you own the land, or (b) certain
designated "sandboxes" or permission is granted for you to build.
You can't own land without a premium ($10/mo, or 6/mo on yearly subscription),
so you may want to start in a sandbox. I subscribed in order to buy my plot.
It turns out for your first purchase of
land you can buy 512 square meters (sort of a homestead) at a steep discount.
Since I wanted to build a virtual Janssen Engineering Building, this was not
big enough.
Off-hand, Second Life's construction tools look like a very reasonable
and general mechanism for making virtual things, certainly much nicer
than writing code by hand, or even having to produce 3D model files, for
everything. It would be interesting to study their
network protocol for such objects.
The axes (z is up, instead of y) is probably
intuitive to everyone except programmers.
LSL
LSL, or Linden Scripting Language, allows users to program the
behavior of objects within Second Life. Scripts are limited to 16KB.
Each script consists of an event-driven state machine, with events
triggered by a rich set of events including: timer ticks, touches and
collisions, chat messages, movement, and other actions.
The LSL is documented at
http://lslwiki.net/lslwiki/wakka.php?wakka=HomePage
LSL is vaguely Java-ish, but not really very similar despite their claims.
Every script is a "finite state machine", more in the software engineering
design sense than the theory of computation sense. The set of events that
can happen is predefined/hardwired in LSL; very different from Java where
you define what methods your object responds to.
default {
touch_start(integer total_number) {
llSay(0,"Hello World");
}
}
Here is Dr. J's first basketball object script. In addition to creating
an object, in order to get it to move he had to click the "Physical" checkbox.
default
{
state_entry()
{
//llSay(0, "I am the avatar of hoops!");
}
touch_start(integer total_number)
{
llSay(0, "Woo hoo.");
llSetColor(<1,0.7,0.7>, ALL_SIDES);
state bouncing;
}
}
state bouncing {
touch_start(integer total_number)
{
llSay(0, "Ho boy.");
llSetColor(<0.5,0.25,0.25>, ALL_SIDES);
llApplyImpulse(llGetMass()*<0,0,15>,FALSE);
state default;
}
}
Learning the set of built-in "ll functions" is easy.
The big deal here is: what
events
are available? For example, the above code throws the ball upwards
every other click; if I want to continuously dribble, what events are
available? Maybe the timer events will do the trick.
Minor aside: Linux computers behind CSAC can run the JEB demo.
Add /net/faculty/jeffery/unicon/.bin.linux to your path to get
Linux x86 Unicon binaries, and then compile away.
SecondLife comments
My plot of land was sized based approximately on the goal of
creating a virtual Janssen Engineering Building. JEB is
approximately 50m wide by 30m high = 1500m. I was able
to find a 1520m rectangle, vaguely close to the right shape, for 17000
lindens, which means it cost me approximately $57 to buy the land
in order to build virtual JEB, not to mention the monthly fees ($10 for
premium account, $8 for this amount of land's "tier fee").
To visit and try building stuff at
Crumbi 234, 50, 41 (or is that 228, 84, 41?),
I have to give you permissions by getting you to join a group.
Some obvious statements:
- the 1520m has a maximum number of primitives
allowed, something like 345, maybe a little higher. This is not enough
primitives for a
"detailed" representation, even though I paid "a lot" of money for the land.
Second Life is a land of crude approximation. C'est la vie.
Why is this limit important for SecondLife?
What does it mean for builders?
- In CVE our entire concern so far has been internal to
our building, but in SecondLife the building must obviously
include an exterior. In fact, the exterior may be kind of
important, and will "eat up" a measurable fraction of our
allowed primitive count.
Also, we have to include primitives for internal connections
that aren't yet present in anyone's .dat but are needed to
provide full connectivity. I am not sure Second Life's
teleport capability is precise enough for us to reach our
unconnected rooms (what a cool idea for dungeons/torture chambers).
Upshot: you may have less than 20 primitives.
- Given this hard limit on primitives, we can't afford to duplicate
any walls between adjacent rooms. The jeb* demos and CVE software
is VERY naive in this regard, not minimizing its primitives at all.
Creating Activities
The only thing better than end-user world-building
is end-user activity building.
In principle, there should be a range of mechanisms for end users to
create things to do in-game. One conspicuous way to do this is to
allow end-users to create "quests", and this has various possible
implementations, but I would like to brainstorm a little for other
ways.
City of Heroes' Quest Creation Tools
So far, I have only tried going on someone else's quest.
Sure enough, it is like an instanced dungeon. By the way,
end users are liable to make impossible quests.
JEB3
www.cs.uidaho.edu/~jeffery/courses/game/jeb3.zip contains a
many-room aggregation of JEB formed by a prior Games and Virtual Environments
class, which was later refined and incorporated into CVE. It may lack
stairwells and connections between rooms, some of which were later added
to CVE.
Avatars in CVE
CVE's Avatar class was designed originally around
hardwired "lego-man" graphics that employ a small number of OpenGL primitives.
Jafar was kind enough to tuck our 3D model-based avatar graphics into
the existing class structure, but there are interface questions.
As originally written, an avatar consists of a vastly simplified bone
structure, with legs and arms having hands and feet, but not
(for example) knees and elbows. A previous student group semester project
did add knees and elbows but up to now their work hasn't been merged into
the main source code base. The programming API for avatars includes the
ability to have them move arms and legs in very simple ways to approximate
walking, raising one's hand, and pointing, but a 3D model might or might not
have a bone structure, might or might not have a pre-existing animation for
walking or raising one's hand, and typically will not do both at once, or
support pointing in arbitrary directions. What to do?
Proposal: add 3D model file "parts" for each avatar body part in the model.
Write a new subclass of Avatar and of Body Part to work off of
(and be populated from) the S3D data.
Notes on the .dat parser/model constructor
The .dat file format was originally a single file; in CVE it is
currently split into two files (dat/nodes/model.dat and dat/edges/static.dat)
but this was a mistake introduced by a student. There are tremendous
advantages to keeping a single-file format. The farthest we got into
the parser before was to see that the world builder sets up a big string
scanning job (of the entire .dat file) and when it finds "Room" it calls
a procedure parseroom() and so on. The parser is a top-down recursive
descent code which builds a set of interconnected objects. Procedure
parseroom first calls some generic parsing code which populates a table
with all the named fields of the room:
procedure parseroom(s,f)
local t, r
t := parseplace(s,f)
It then builds a room object:
r := Room(t["name"], t["x"], t["y"], t["z"],
t["w"], t["h"], t["l"], t["texture"])
Mapping the table t (all contents of the .dat) to the Room r is a
double-edged sword. On the pro side, fields in the .dat can be in
any order, and extra fields cause no harm. (If a field is missing
from a room, the Room constructor had better have a default it can use.)
On the con side, an
extra memory copy is happening here that could be avoided if the
instance itself were passed in and populated. parseplace() is
highly polymorphic (one code used for many types of objects composed
from fields) and that would complicate its internals.
Procedure parseplace() builds the table (a set of fieldname keys and
associated values). The place is terminated by a "}", when it is
by itself and not part of a field (parsed here by parsefield()).
procedure parseplace(s,f)
local t, line
t := table()
while line := readlin(f) do line ? {
tab(many(' \t'))
if ="}" then break
if &pos = *&subject+1 then next
parsefield(t, tab(0), f)
}
return t
end
parsefield()
grabs a field name (delimited at present by
space/tab characters),
which will serve as a key in the table. It then calls parseval() to parse
a value, which may itself be a complex structure.
procedure parsefield(x,s,f)
local field, val
s ? {
tab(many(' \t'))
(field := tab(upto(' \t'))) | {
write("fieldname expected: ", image(tab(0)))
runerr(500, "model error")
}
tab(many(' \t'))
val := parseval(tab(0),f)
if field=="texture" then val := world.find_texture(val)
if (field == "action") then {
/(x["actors"]) := []
put(x["actors"], 1)
}
x [field] := val
}
end
A value by default might simply be an arbitrary string after the fieldname,
extending to the end of the line. There are three special cases which have
more complex semantics: a numeric constant, a Wall object, and a list.
procedure parseval(s,f)
local val
s ? {
tab(many(' \t'))
if val := numeric(tab(many(&digits++"."))) then return val
else if ="Wall" then return parsewall(tab(0), f)
else if ="[" then return parselist(tab(0), f)
else return trim(tab(0))
}
end
If we chase inside parselist() we would find that other virtual objects must
appear inside a list object, while Walls do not. It seems odd (and bad
design, basically) to single out Wall() here as a special syntactic entity.
Dr. J should add additional notes here on parsewall() and parselist().
The recommended way to test your room data + textures is to run your sample
data on the jeb1 demo. At least one student reported the jeb1 demo not
running for them on Windows. If ran for me on Vista laptop and on XP on
VMware on Linux... but if you are having difficulties, see me for help, or
try another machine. Oh: what image file formats are you trying to use?
.gif is safe. .jpg and .png are "maybes". libjpeg worked on linux and not
on windows last time I checked. Jafar claims we have libpng support, but
not sure if that's gotten built into windows yet or not, either.
Virtual CS Project Update
Earlier I pointed you at jeb1.zip as a demo; there are progressively
lengthier demos named jeb2.zip and jeb3.zip. jeb3 has a program
named modview that helps with x-z coordinate calculations, given a floor
map of the building.
Idea: the .dat file reader should add some semantic error checking,
besides needing better syntax checking. For example, if an obstacle or
decoration of any type extends beyond its room boundaries, this might
be flagged as an error.
Lecture 26. Virtual Environment Server and Network Protocol
Announcements
- No class next Tuesday !
- I will be in Idaho Falls visiting UI and
giving a lecture there in CS578 (neural networks design).
I am looking into whether I can do a lecture topic of mutual interest
(biologically-inspired game A/I) and get it recorded for you-all, but
as of now, no class.
- Flip Flop !
- Earlier I said we'd do in class demos on May 5, but I think 75 minutes
will not be enough time. Since the final exam will be a take-home exam,
I intend to use our final exam period (Wednesday May 11, 12:30-2:30)
for in class demonstrations and discussion. I will post the take-home
midterm exam on Thursday May 5 but it will not be due until Tuesday
May 10.
- CVE update
- New Windows binaries are posted. For source code, the svn repository
holds the latest.
Rabin
The schedule says we are supposed to cover THREE (3!) Rabin chapters
this week. Happily, 2 of those chapters' slidesets are very short.
The Server and Protocol
Up to now we have been single-user and a game engine would have served
us better. CVE has connections to a server, with chat, avatar interaction,
collaborative IDE, etc. Lecture 15 was an overview of Unicon's networking
capabilities, which basically boiled down to: almost as easy as reading/
writing text to local files. Let's talk about the server some more.
Developing
n-User Chat
Capabilities
From "chatting" we have to make the big leap to seeing each other.
But let's start with just "chatting". Some notes about this demo:
- chat window is utterly stupid, doesn't use GUI classes
- server seems to recognize logouts pretty well; may need more work
- client does not currently handle disconnects/reconnects
- fairly easy to reorganize the main event loop to where it feels slow
Server State and Network Protocol
What information is needed on the server?
How shall the server store that information in nonvolatile memory?
Lecture 27.
Announcements
- Class next Tuesday: "Biologically-inspired Algorithms in Game A/I"
- Joint with CS 578.
If you have no other class at 12:30-1:45, you can attend in JEB 026
at that time. Otherwise, I think EO will make a video available, but
since it is irregular, it will take some manual effort and a longer
than normal time to get that video in place.
- CVE Class Time Online
- We will hold a class session in CVE a week from today (April 28).
We will do it in the regular classroom, probably for half the session.
Please check your timestamps and update to a new/current CVE client
before then, possibly the night before or the day of the session.
The goal will
be to facilitate the completion of the hw-cve.html assignment, and
since this is research software, we see how many crashes and how
many reboots we will get.
- HW#3
- Have read through the code and nearly finished grading, except for
mechanics/playability/strategy points.
(graded: Stinger/Kimball/Mazur/Vollmer/Nance/Miller/Norris/Flynn).
If you turned in a HW#3 and aren't on this list, please double-check
with me to see if your HW got eaten by the UI mail-troll.
System Challenges for Multiuser Online Virtual Environments
- Glueing together the solutions for individual components of the problem is
not guaranteed to work.
- Latencies are inevitable; consider how to deal with them from a
user's perspective.
- Distributed Legible City - (multi-user) bicycle VR with audio link.
What system challenges does this reveal about VR-oriented VEs?
- Engineer something to do, or something to care about. WoW or LOTRO
fall flat for some users because their many things to do are irrelevant.
Many VE's have far less to do than do the MMO games.
Of course one can talk about the VE as a technical or artistic
achievement, but what else?
- The behavioral quality lacking in VE's isn't due to lack of high-powered
AI but due to the need for simple reasonable behaviors to be modeled
for large numbers of everyday real-world objects.
- Environmental properties - how much ordinary behavior can be applied to
all objects, because it is a property of the environment?
- Use multiple inheritance
(environmental behaviors + object-specific behaviors)
- not just an Environment: a range of environments.
Suggests either: (A/I) generation algorithms, or user-creation,
may be more attractive than the hundreds-of-pro-artists dev model
- Explicitly separate the (virtual world/simulation) model, from each
user's perception of it.
Understanding Network Requirements for CVEs
Idea: in virtual classroom, people sit down -- that means the network
and server don't have to worry about transmitting movement events while
they are mainly transmitting audio and/or video instead. Could this
be exploited? Better VOIP if you get everyone to hold still?
- Almost all network requirements derive from user behavior.
- Do users speak at the same time? That takes a more bandwidth than
simplex audio
- Users are moving 15-45% of the time, and speaking 5-30% of the time.
Can we use that to calculate bandwidth at the server? Greenhalgh's
numbers are around 2KB/sec for motion (for 6fps), 2KB/sec for audio.
This would be marginal on a 56Kbit modem (40Kbit connection = ~4KB/sec).
Greenhalgh mentions that other systems economize for motion.
Jeffery suspects VOIP often uses > 2KB/sec
- Peak bandwidth will be higher if users' activities are highly coordinated.
For example, if users are all instructed to do something at the same time.
- Three key actions: user movement, user audio, and discovering new objects
and users. Invent methods to make these cheap; invent methods to scale
them to users' bandwidths.
- Design a system to be as general as possible; only optimize heavily-used
parts.
- DIVE idea:
use standard WWW servers to serve up our data?
Alternate: use a SQL database to serve up our data?
- MASSIVE-1: on 10Mbit ethernet, maxed out at 14 users
Distributed Architecture Implications
- indoor vs. outdoor
- zone structure
- who can see what and whom
- how many users? (more requires being able to restrict what's transmitted)
- the dynamics of the world (can you blow up walls?)
- the extensibility of the virtual world - can new objects be introduced
on a live server, or only by shutting down, recompiling and sending out
new executables to all users?
CVE Extended Demo
Today's demo will just try and visit every menu item and every function that
we can manage, to see how many times we can get things to crash, and how many
features/functions we can use successfully. We will keep a running count.
CVE: Following Execution Across the Net
When an avatar is moved, besides the 3d display
list graphics tweaks, protocol strings corresponding to those moves are
"queued up" to be sent in a batch over the network,
and marking that the local client world needs refreshing.
Some number of
put(grouping, moveuid || "part " || name || " " || dir || " " || ang)
are followed (at the end of actions()) by a call to flushnet()
method flushnet()
if session.isUp() then {
session.Write(grouping)
grouping := list()
}
end
Session's Write() method bundles up a list of strings as a single string,
so it gets sent as a single packet. Where does it go then?
The server receives these commands... and does what?
# server.icn::run()
if not (L := select( socket_list, Ladmins )) | *L=0 then next
...
if buffer2 := Tsock_pendingin[sock] || sysread( sock ) then {
...
buffer2 ? {
while buffer := tab(find("\n")) do {
ExecuteCommand( sock )
...
"move": {
dynStHandler.saveAvatarState(Cmds,sock,Tsock_user, parsed[2])
dynStHandler.getRecepientUsers(Cmds, sock, Tsock_user,
Tuser_sock, TrecpUser_sock,
"AvtMove",parsed[2])
sendtoSelected(sock, TrecpUser_sock, parsed[2], "move", 1)
Saving state involves writing to server local disk.
getRecepientUsers is another matter.
CVE Source Code: the Next Level of Detail
Let us take a look at: nsh.icn, nshdlg.icn, nsh-world.icn,
and the scene graph files.
Lecture 28. Research Topics in Virtual Environments
Biologically-inspired algorithms in game A/I, delivered from Idaho
Falls.
Lecture 29. Research Topics in Virtual Environments
Rabin
The schedule should be rearranged next time to work this chapter in earlier,
as part of the 3D graphics section. It is only a few slides, and it has some
useful ideas.
Reading Assignment
- Read the evogames papers from Tuesday's lecture on game A/I
- Check out cves.html.
Read the paper on network traffic (Wolff, Roberts, Otto).
- find me at least ONE new link that is not on my cves.html site that
is about virtual environments, that is, shared 3D persistent online
multiuser applications (first come first serve).
-
On your final exam, be prepared to discuss the ideas from these (the evogames, cves.html,
and "Research Links" forum topic) papers.
Google around for "virtual environment", "artificial environment", etc.
Network Protocols for CVE's (Greenhalgh)
- Unicast vs. multicast (IP multicast, Deering and Cheriton)
- Reliable vs. unreliable (unreliable OK for audio!? unreliable OK for
moves)
(what properties of a given communication make it OK to be unreliable?)
- selective reliability
- Disadvantages of client/server: server will do more processing than a
multicast router would. server limits scalability of the system.
- Greenhalgh's experience: servers haven't been a major bottleneck.
- Advantages of client/server: single network connection, more effective
congestion control, more reliable, ability to combine/piggyback messages
to a given user, server can perform extra processing/filtering on messages,
for example, summarize a sequence of messages in a single message.
- General heuristic: use multicast for unreliable location connections,
client/server for everything else
Lecture 30. Future Trends in Games and Virtual Environments
Last Tuesday's lecture?
I have formally requested that last Tuesday's lecture be made available to you-all, but the
procedure is more tightly controlled (and therefore delayed) than I would have liked. In the
mean time, here are the talk slides. I am not going through these again today, but want to point
you at the slides listing papers of interest from the evogames conference.
Class Project Demos During Scheduled Final Exam Slot
Our final next Wednesday, 12:30-2:30pm.
120 minutes for 15 students == 8 minutes per student (teams get the
sum, 3-person team gets 24 minutes), including
setup/teardown. We will need to setup in advance,
let's talk about platforms.
Games and Virtual Environments Virtual Expo
I would like to build a webpage showcasing your projects and those
of your homeworks that are "finished" and "end user playable".
However, this is not required as part of your grade. After the
semester is over and grades are turned in, please let me know if
you would like to place one or more of your programs in this public
forum.
Presence in Shared Virtual Environments (Buscher et al)
Should different vendors' virtual worlds know about each other and
interoperate? IM systems have gradually veered towards being able to send
messages across platforms... Should our model of other users in the CVE
include sending and receiving messages via regular internet e-mail and such?
In the old days, on a shared UNIX system where the entire department used a
single machine, I could easily tell who was logged in, whether they were
away from their keyboard (idle time), and maybe even what application they
were running. If I had that user's cooperation, how easy would it be for me
to check whether one of you was using a computer (not in the CVE)?
Methods of locating someone else in virtual space. Some of these are
graphical, some textual, and some could be either. Perhaps some CVE's would
eschew some of these techniques in order to be "realistic". What other
methods can you think of?
- Landmarks ("meet at the bridge")
- Coordinate systems ("meet at 538,-111")
- Virtual "GPS" (computer provides pointer to any person you specify)
- "Yelling" (computer provides approximate direction of person when they ask)
- Teleporting them to you, or you to them
- Peeking (being able to look at their screen, i.e. through their "eyes")
"corpsing" -- when someone is away from their keyboard, but you have no way
of telling that.
The Problem with 3D Environments
Navigating in 3D quickly becomes so hard that we can't concentrate on other
tasks. I have seen this recently in 3D modeling tools, and have seen it
before as a challenge to people trying to design a 3D virtual mouse widget
for navigating. How do we solve this problem?
- Reduce 3D down to 2D navigation (most of the time)
- Provide automatic navigation (most of the time)
Conundrum: put people and information together in collaboration spaces that
inform people about what is happening...without preventing us from doing our
real work.
Themes
Avatars
How users represent themselves to each other; the degree of realism and
level of detail available for communication purposes; the presence of
computer-controlled virtual persona.
Bandwidth, scalability, and fundamental limits of hardware
The level of network connection, the number of users, and the capability
of graphics hardware are all intertwined to affect the quality and
usefulness of a CVE system. I am also interested in the complexity and
difficulty of writing CVE software as a limiting factor, and of developing
languages and tools to reduce that difficulty so that richer CVE's can
be developed.
Augmented virtual reality
How much real-time real world information can be made visible in a CVE?
Obvious Things About Commercial Game Virtual Environments
- MMRPG's are a spectacular technical and commercial success. Everquest
introduced or popularized many technical concepts as well as a genre
and business model which others like City of Heroes have imitated and
improved upon. The per-boot update patching model is a good example.
- These games are very large, complex, expensive, and demand a high powered
computer, especially a powerful graphics card. Everquest II will not
run on my laptop with 64MB dedicated NVIDIA 3D chips; WoW does, and so
does City of Heroes. Guess which games I am more likely to subscribe to?
- These games use surprisingly little network bandwidth, but latency is
a huge problem, as is server scalability for large numbers of users
- MMRPG's are high-powered chat engines.
There are multiple simultaneous sets of users
you are chatting with: individuals, people in your
immediate vicinity in 3D, people in your "zone",
people in your per-session "group",
people in your persistent "guild"
- The graphics and chat are competing with each other for space and
user attention.
- The world is real-time. Chatting can get you in trouble if a
monster comes up and starts attacking you while your keyboard is
tied up in a long chat.
- The graphics has a lot of static stuff that just sits there and
doesn't "do" anything...pro: makes the world more "immersive";
con: users can't burn it or chop it down, blow it up, etc. and
this detracts from the realism. Newer games have less static stuff;
Half-Life 2 brags about how you can destroy everything you see.
- Playing the game over a period, your character in the virtual world
becomes substantially more powerful. The main Thing to Do is to try
and acquire more power and/or more fame amongst your peers.
This is a mixture of "experience" mainly
acquired by killing monsters, and acquiring powerful magic items.
- The "zone" system really detracts from the realism
Unobvious Things About Game CVEs (MMOs)
- You pay $12-15/mo, it is not obvious that it should cost this much
- You pay periodically for new content (new zones, new creatures),
the monthly fees are mostly pure profit.
- The graphics have gotten a Lot Better over time; machine requirements
have gone up more gradually
- Success == More Players == Worse Gameplay == Drives people away.
Scalability limits are partly social.
- The "Missions" in City of Heroes succeed in some senses, but are
a major failure in many ways. They give players something
to do, but because Everyone can do them, they violate the
immersive realism, warp (verging on ruining) the economy,
and cause exhaustive off-line cheat-reference-websites to
be an important (nigh unto essential) part of serious gameplay.
- People play the game very differently to meet very different
personal needs. There are people escaping real life, people
seeking human contact, people trying to feel competitive/superior...
- Grouping with total strangers for random acts of violence is not
realistic...but is by far the best and sometimes only way to advance.
Play would benefit from an ebay-style alignment rating system.
- Because everything in the world is static except the players, the
immersive reality is compromised by e.g. killing the same person
or monster over and over again.
- Because players can't affect changes (build houses, take over cities)
the world experience is controlled by Sony, not by players, and the
rate of new content is slow, and the place becomes boring over time.
"Environment" Imposes Some Essential Features on CVE's
Last lecture, CVE's were portrayed as multi-user 3D applications.
But not every multi-user 3D application has to be a CVE, and based on
the following feature list, some non-3D applications are "almost" CVE's.
- a CVE has a virtual world
- This is a large space, typically with many locations and objects.
Its goals are: make the user feel they are in that place, and make
the user feel that within that place, they can accomplish their
goals.
- a CVE has time, and persistence
- Things that happen in one CVE session affect later sessions.
There is no "pause" button; the virtual world is happening whether
you are there or not; things happen while you are out.
Users do not ever "start over".
- a CVE is reactive
- CVE's content is user-controlled. Users do whatever they want,
rather than following a script.
Additional "Desirable" Properties of CVE's
- a CVE should be "realistic" as possible
- laws of physics, day/night, weather, and graphical realism are all
examples where the CVE doesn't have to work the way the real world
does...but most CVE's will be more "immersive" and understandable
to users if they reflect the real world.
- a CVE should scale to handle "as many users as needed"
- other people are a primary aspect of our real environment, and
allowing only 4 people, or 16 people, isn't very realistic
- a CVE should be richer than necessary
- is it a "place", or just a chat/teleconferencing program?
Anatomy of a CVE
What technical pieces must be in place to make a CVE?
- Users must be able to communicate
- This implies Internet or other transport layer, plus common language or
robust translation capability. "Language and translation" apply to both
the network communication protocol and the human users.
- Clients must be able to "render" the virtual world usefully
- This implies sophisticated graphics software and a minimum hardware
platform. In this class, I believe we can obtain or write sophisticated
graphics software, but let us ask: do we all have access to minimum
hardware?
- Users' views of the virtual world must be (somewhat) consistent
- There is a big problem if the time grows too long between your doing
something and the time other users see what you do (latency). Some
actions are more important than others in this regard.
New CVE Concept of the Day: Goal Assistance
We didn't see, in the City of Heroes demo, an example of a mission,
but essentially
a "mission" is a goal the game offers to the player: if they complete a
certain task (maybe delivering an item to someone who needs it, or finding
some lost artifact, or defeating some villain), they will receive a reward.
In real-world-based CVE's, there may not be "missions" but there may still be
goals, such as: complete a homework assignment so that it passes an automatic
submission tester.
One interesting point is: people cannot always remember the details of their
goals: where to go, what to do, etc. They sometimes wind up writing down the
instructions they were given by a (computer-controlled) character in the
game. It is sort of obvious that the computer should provide assistance
with this task, provide some of the capabilities of a Personal Digital
Assistant, such as a Todo list. City of Heroes does this rather nicely.
Scalability of CVE's
Around the year 2000 the scalability limits reported in [Churchhill, Ch 2] were
something like 8-64 "mutually aware" users. Everquest zones are not dissimilar,
somewhere between 50 and 100 users, the zone performance becomes unpleasant.
Robinson et al distinguish between upward scalability (more people) and
sideways scalability (different people). Besides scaling users and groups,
they argue for more different kinds of objects in CVE's, especially objects
with real-world presence (machines, printers, files), where manipulating
the object in the CVE causes real-world work to get done.
Should all this work happen inside the CVE? Robinson argues for the 3D
part of a CVE to be only one of many different collaboration programs,
complementing other forms such as document viewers, web, and audio/video
connections. In support, they observe that the 3D CVE's usually overemphasize
the people, while other applications usually underrepresent them.
They are arguing that all our mainstream applications should become CVEs
and propose a VIVA architecture along these lines. There are many pros to
this approach, such as accessibility and interoperability when 3D graphics
are not available, from the web or a PDA, etc. What are the drawbacks
to trying to make all our regular applications CVE's? Do Robinson et al
identify those drawbacks?
Videoconferencing: a perpetual prince, but never a king?
"Phone and email continue to grow exponentially, while videoconferencing
use remains flat" - why is this? Are CVE's bound to have the same lack
of adoption as videoconferencing? When video is getting used, it is not
to look at the people but to look at the objects they are working with.
What does this say about CVE's? Objects and conversations about them
need to be seamlessly connected.
Other ideas:
- A lot of energy is devoted to describing how they allow people to be aware
of other people visiting the same webpage at the same time (big deal).
- No Fixed Starting Point; VR, web, word processor, etc.
are all equal citizens of their CVE.
- VR's main or only value: background awareness of others.
(Do you agree, or disagree?)
- Server-based
architectures are limited by the server CPU, while peer-to-peer based
CVE's are limited by the network capacity.
- Some CVE's support 3D if available; 2D otherwise.
Besides "master servers", VIVA uses at least 6 kinds of special-purpose
servers. Traditional services of "VR servers": spatial data processing,
collision detection, awareness and authorization services, environment
partitioning. Dynamic repartitioning is seen as central to scaling to more
users.
[Snowdon96]: A review of distributed architectures for networked virtual
reality. Virtual Reality: Research, Development, and Applications 2(1),
155-175. gives a reference architecture consisting of:
- distributed system services (name server, trader, time service,
resource discovery agents)
- security services
- object support services
- core VR services, e.g. collision detectors, world servers
- non-core VR services: application object
- user interface services
- other supporting services
Where we are at in the course so far
We haven't found all available CVE's that are out there on the internet, but
we have found a number of them. Good news: I will continue to award points
on HW#1 for new sites or tools you find. Bad news: I will not consider your
HW#1 completed until you have tried out some CVE from the sites we've found,
been online within the CVE long enough to gain some experience with its
graphics and communication facilities, and report on your experience in class.
Notes while trying to test your HW#K
- What, Binaries?
- Of course I am very uninterested in binaries,
I want source code and any resources (e.g. .gif files) bundled up
(.zip is probably best, .tar or .tar.gz or other common formats OK).
- Missing makefile?
- Turn in everything I need to compile and run your program correctly,
if you can. This would include a makefile or batch file in the .zip
that you turn in.
- Runtime error?
- It is not uncommon in Unicon to get runtime errors, don't be panicked
by them but do get practiced at reading them. A sample one is
Run-time error 107
File cve.icn; Line 244
record expected
offending value: &null
Traceback:
main()
make_SH167(...parameters...) from line 73 in please8.icn
Room_add_door(...parameters...) from line 19 in please8.icn
{&null . coords} from line 244 in cve.icn
Now, here are some screen shots from last year for tools I was able to run:
Lecture 31. Future Trends in Games and Virtual Environments
CVE Status Report
- Hattrup added to CVE, Riley in progress, Jafar on deck.
- If what your room looks like in CVS is better than what you turned in,
it will replace it for HW8 grading purposes.
- Multi-user / account creation somewhat more functional
- CVS and www.cs.uidaho.edu/~jeffery/setup-cve.exe updated
- Generalization from NMSU required reduction in "two-dimensional thinking"
CVE's Using Symbolic Acting (McGrath/Prinz)
- "chance meetings", "recruited conversations"
- sensing what people are doing and when they are available to chat
(how do we build automatic "available to chat" sensors into our CVE?)
- ignoring people politely when we are busy
Symbolic Acting
You don't have to control your avatar's appearance and gestures, the
system does it for you, based implicitly upon your activities. The
avatar represents you to others (its automatic actions symbolic of
your real actions). Example: when a window is on top of your 3D view,
your avatar appears to be reading a document. Put people together based
on subject matter: If you start editing a
.c file, your avatar might automatically head to the virtual C lab, so
others working on C can notice you. Alternatively, you might put people
together based on their activity (analogous to meeting by the copier,
or meeting at the drinking fountain).
In between silence and talking there is a continuum comprising mutual
sense of presence, body movement, mutual gaze awareness, and the trajectory
of body motion (towards someone, away from them, on a route unrelated to
them, etc.). "Sleepy mode": avatar looks like an ice cream cone.
Contact Space and Meeting Space: the lounge versus the seminar room.
The big difference is whether others can interrupt.
Nessie world: different rooms for different working contexts. Avatars are
Lego puppets. Agent avatars signal time of day (waiters, janitors?),
active virtual furniture shows external values and activities
(temperature? stock values?). Projecting the CVE in the background: on the
wall, or maybe on the root window?
Experience results: the "meeting space" won't be the focus during meetings,
the focus is on the material presented, documents being reviewed, etc.
People will want to customize their avatars, but do not need (or want) them
to look exactly like in real life.
Who is talking in the CVE? Small window size means this vital information
may need to be exaggerated.
When is symbolic acting unfortunate: when it embarrasses you publically,
because you want to quietly working on something else (say, surf a website)
while in a meeting in the CVE in another window. Sometimes you don't want
the system reporting your every action to others!
More issues: security can be a problem; no one wants to have another account
to login to; contact space needs to cooperate/integrate with e-mail, telephone,
etc.; contact space needs to be accessible via PDA/cell phone, etc.
The Forum is not just chat, it is the ability to comfort, monitor, increase
awareness, and observe others.
Dr J's idea of the day: "selecting" (clicking on) another avatar with your
mouse might send an acknowledgement message to the other person, to let them
know you are looking at them and they have your attention, as a precursor,
if they wish to chat.
More on Modeling 3D Spaces
So far, you have constructed simple models of a single room, and we have
(perhaps by today) normalized our coordinates such that, if we did it right,
we could stick all the rooms into a single application and form a space
with a number of rooms.
An additional major topic will be: how to create 3D
objects and avatars, and implement a
persistent state for the virtual world.
This course is not about 3D Graphics: we won't be covering advanced algorithms
for photo-quality rendering like they use in the CG movies. But, everybody can
learn enough 3D graphics to be useful for your project.
Some 3D Geometry
At some level every object in the 3D space, including floors, ceilings,
and walls, must be represented in a geometry system as a set of polygons,
each of which has a set of (x,y,z) points and some attributes to specify
its physical appearance, such as color and texture.
In practice, complex objects are composed from simpler objects. Each
simpler object that is a part of the more complex object is given by
specifying their location and orientation with respect to the complex
object. This is how, for example, you might attach an arm to a torso,
or attach different pieces to a table or lamp or whatever.
Location and orientation are more generally given by the operations
Translation, Rotation, and Scaling. A basic result of early work in
computer graphics was to combine and apply all three operations via
a single matrix multiplication. We don't have to write the matrix
multiplication routine, see CS 476 or a linear algebra class for that.
We can just enjoy the fruit of their labors as manifested in our 3D
graphics library (OpenGL) and a higher level API built on top of it.
At some point the "outermost" objects (say, an entire table or an entire
person) are placed into the virtual world by similarly specifying the
object's location and orientation with respect to World Coordinates.
Rendering an object in room coordinates example:
PushMatrix()
Translate(o.x, o.y, o.z) # position object within World Coordinates
o.render() # object rendered in Object Coordinates
PopMatrix()
Note that if a subobject is rotated relative to its parent object, the
rotation will look crazy unless the subobject is first translated to
the origin, then rotated, then translated back to its intended position.
Drawing Primitives
There are a lot of
drawing primitives available besides the
FillPolygon() function we have used almost exclusively up to now:
DrawCube(), DrawCylinder(), DrawDisk(), DrawLine(), DrawPolygon(),
DrawPoint(), DrawSegment(), DrawSphere(), DrawTorus().
These primitives are further flexified by the Scale() function.
When stretched (via scaling), primitives like DrawCube() can handle
any rectangular shape.
Lighting and Materials
Not all objects need to be textured. In fact, given how expensive
textures are, and how limited a resources they are, we probably
ought to avoid textures except where they are necessary, i.e.
when an object has a mixed or rough surface texture, or when we
have a special situation.
OpenGL has 8 lights, which can be turned on or off, positioned
at specific locations, and can feature any mixture of three
different kinds of light: diffuse, ambient, and specular.
Diffuse seems to be the dominant light type, with the others
modifying it.
In the example:
WAttrib(w,"light0=on, ambient blue-green","fg=specular white")
Objects would look their normal (diffuse) color given by their
foreground ("fg") attribute, except there would be a bit of blue-green
on everything from the lighting, and objects that have very much
shinyness (read your manuals!) will reflect a lot of white on the
shiny spots.
In addition, if you are not using a texture, the "fg" attribute
for an object can include an object's appearance under the
three kinds of light, and can include a fourth kind of light,
emission, where the object glows all on its own.
Fg(w, "diffuse light grey; ambient grey; _
specular black; emission black; shininess 50")
One thing that was added recently to the 3D facilities is the
ability to blend the texture and the fg color when drawing
an object ("texmode=blend"). One thing that is going to be
added in the future (as soon as I get a student to help) is
to add a set of predefined / built-in textures
( "brick",
"carpet",
"cloth",
"clouds",
"concrete",
"dirt",
"glass",
"grass",
"grill",
"hair",
"iron",
"marble",
"metal",
"leaf",
"leather",
"plastic",
"sand",
"skin",
"sky"
"snow",
"stone",
"tile",
"water",
and "wood").
Bad News? Midterm Exam?
There will be a midterm exam, on XXXday, XXX YY. It will be a short,
easy exam based on CVE text chapters assigned, plus Unicon language stuff
based on subjects relevant in your homeworks.
Introduction to Networking in Unicon
Today we start on networking. In order for our CVE's to be collaborative
multi-user applications, we must tackle the networking communication
aspects.
Reading: Unicon book, chapters 5 and 15.
Networking support in Unicon was designed by Dr. Shamim Mohamed (Logitech,
Inc. of Silicon Valley) with a little help from Clint Jeffery, implemented
for UNIX by Shamim Mohamed, and ported to Windows by Li Lin (M.S. student)
and Clinton Jeffery. These capabilities are simple, easy to use
communication mechanisms using the primary Internet application protocols,
TCP and UDP. Unicon also has "Messaging facilities", providing support
for several popular network protocols at a higher level than the network
facilities (HTML, POP, ...), done by Steve Lumos (M.S. student).
Networking for Non-nerds
The Internet is very simple (ha ha), it is just a connection between all
the machines that are connected, and a set of routing rules for how to
deliver messages. Not counting the routers that just pass messages around
in the middle, there are fundamentally two classes of machines:
clients which mainly initiate connections on behalf of users, and servers
which provide information. Messages are routed through the internet
using IP numbers. Clients' IP numbers are often transitory,
and used only to connect internally to a gateway within an organization
in order to start an outgoing information session. Servers usually
have a fixed IP number, visible either only within an organization
behind its firewall, or on the public Internet where they are subject
to hack attacks of all types, but where as a group they constitute the
main value the Internet provides.
Besides the IP number identifying a particular machine, most Internet
services specify a port at which communication takes place;
the ports serve to distinguish different programs or services that
are all running or available on a given server. The ports with small
numbers (say, the first few hundred) have standard services associated
with them, while higher numbered ports can have arbitrary server-defined
associations to custom applications. Ports providing standard services
can usually only be run by the administrator of a machine; ordinary end
users can generally use higher numbered ports.
Client-server and Peer to Peer
A peer-to-peer system is just a system in which clients are servers.
This only works if clients' IP numbers are visible to each other.
Peer-to-peer systems generally have to use a central server to find
each other, and possibly to forward information back and forth between
two clients neither of whom can receive incoming connections due to
firewalls.
Configuring CVE's for Object-Focused Interaction (text Chapter 7)
OK to skim this chapter.
- Engage with and share visible objects in the 3D environ
- many activities require talking and monitoring each others' conduct while
looking at and using some workplace artefact.
- Earlier we said, selecting someone else should tell them they can chat
with us. And we've said, we should see each others' cursors moving around
within a shared text editor. More broadly this chapter is saying, we
should be able to see what object others are selecting when it is not us;
other users should not just have an avatar, they should have visible
lazergaze, or a VR cursor. In RL we detect all this by watching each
others eyeballs, but it will be hard to manage that in a CVE, we have
severe resolution loss.
- Embedded video views that are dynamic texture maps attached to
virtual objects
- Avatars should be able to point their arms in any direction they please;
head should tilt to face that direction, to reinforce pointer.
Lazer gaze, or glowing or highlighting the object-gazed upon, would
be a more extreme method.
- Narrow field of view (55-60 degrees, 1/3rd of humans' normal viewing
angle) can often cut out visible features of the other avatars
(tunnel vision) (compare with driving a car; we need turn signals
and frequent manual checks where we turn and look behind our car).
- Slow speed of movement makes it worse; harder to make quick glances.
- "peripheral lenses" extend avatar's field of view
- Beyond lazer gaze, other users fields of views can be made visible
by making avatars into giant flashlights.
Startup Time
A lot of the load time for please8 is apparently decompressing and RESCALING
the GIF textures. We can speed things up dramatically by trimming our
textures smaller, choosing powers of 2 (not 640x480, 512x512 or 512x256),
and possibly including the textures in the executable or in an uncompressed
(or less-computationally-intensive compressed) format.
Tiling textures
To Shrink your textures you must tile them. To tile them, use texture
coordinates > 1.0. For example (0,0, 20,0, 20,20, 0,20) repeats a texture
400 times over a rectangular surface. Our walls and related classes need
a parameter to let us tweak this number; furthermore, each texture has an
ideal size (say, 1x1 meter, or 0.1x0.1 meter, or...) and the tiling
factor should be automatically calculated from region size divided by the
texture ideal size. A 6.5x3.0 meter wall would want tiling factors of
65 and 30 for a 0.1x0.1 meter texture. Note x and y factors are different.
The presence of ideal x- and y-size factors in addition to the actual image,
suggests a texture database and a texture class will be useful to us.
Making Textures Tile Properly
- http://www.highpoly3d.com/writer/tutorials/tileable/seamless.htm
- http://astronomy.swin.edu.au/~pbourke/texture/tiling/
Viewing Volume
In my multiple rooms prototype, I noticed in longer corridors that I was
not rendering the whole scene, the far away stuff was clipped. It turns
out the Unicon runtime code needs to be extended to give us control over
the "viewing volume", and that extending it to so faraway stuff is trickier
than it sounds.
How Not to be Objective (text Chapter 8)
It is OK to skim this chapter.
- Peripheral Awareness
- Informal Meetings - how to rig things to make this happen more often?
- Learning by Watching - how to make it easier to watch others do tasks?
Subjective virtual environments
Shaping landmarks to make certain encounters more frequent;
users with different access levels to data;
users with custom views/displays; task-specific displays
(e.g. an electrician's view of a building);
multi-lingual CVE.
Flexible Roles in a Shared Space (text Chapter 9)
Please read this chapter
"Kansas" - 2D, programming environment for the language "Self".
Self is a delegation-based descendant of smalltalk.
"Field of view in most CVE's is so narrow that other avatars are usually
off-screen".
- Roles may be implicit or explicit
- Roles may be temporary, have term duration, or be persistent
- Roles may be supported by explicit tools or locations
- Roles tend to evolve/change over time
- Roles may be associated with awareness information categories
Capabilities - an old concept from the 60's. A capability is an object
representing the right to access a protected object. Capabilities can
be passed/delegated to others. Capabilities store what kind of access
is granted, plus a reference to the protected object; they follow a
transparent forwarder, or facade paradigm.
Capabilities could be used anywhere in a CVE, but perhaps the user interface
is a sufficient place for them. A capability can itself have a visible
manifestation (like the piece of chalk, or the microphone). A "capability
tray" might hold (and allow easy sharing) of a user's entire capability set.
Avatars
We have already talked some about avatars this semester. Here are some
additional thoughts on them.
(you should read this article, from the ACM CVE 2002 conference).
Furnas is a major pioneer in the Computer Human Interaction community.
This lecture reflects my thoughts while reading their article.
- social presence
- how you look shapes/influences how others will treat you
(social conventions)
- Yo's avatar
- Yo Sep has got an avatar that with a single stroke (head images)
achieves more reality than the CVE in this 2002 paper.
- ants and giants
- it is easy for us to exaggerate differences in size, if we have a
reason to do so. If you want to see microdetails, become an ant.
If you want to walk from here to Albuquerque and see the Organ
and Jornada ranges in a single hike, become a giant.
- multiscale collaboration
- for large complex structures, different collaborators may need to
work/view the world at different scales (seeing different levels
of detail). Example: software architects vs. designers vs. coders
- dynamic sizes
- a user might shrink or grow themselves to fit a situation
- size in social domination
- bigger individuals tend to dominate social situations
- size in natural life
- We all start out life as "newbies", and gain in size and ability.
Ability in a CVE might include: movement speed, viewing distance
and detail, having keys to certain rooms...
- "size" in unnatural CVE life
- More avatar upgrades might include
ability to teleport, walk through walls, fly, etc. Besides
size, avatars with more abilities might stand out
via color, glow, louder (Jurassic Park-style) strides...
- communication-centered vs. artifact-centered collaboration
- CVE balances and supports both, but it is artifact-centered
collaboration where it will shine or fail.
- but avatars need (in general) to be visible
- bad things happen in CVE's with invisible users
- external min and max?
- others' view of you may need to be capped, even if your own view
of the world scales wildly
- scaling is more than just calling Scale()
- in general, smaller = render fewer details please. wire frames?
- proxemics
- study of proximity. Asymmetrical proximity when avatar scales
are varying. Giant will "feel" that ant is far away, ant will
"feel" that giant is real close.
- proximity ranges:
- intimate = < 0.45m
- personal = 0.45-1.2m
- social = 1.2-3.6m
- public = > 3.6m
- Two (or more?) avatars?
- One for action, one for conversation? Daemon avatar, a placeholder
for conversation (intercom) while one is elsewhwere.
- Changing viewpoints
- See from your daemon's eyes; see from others' eyes to know what they
are looking at/talking about/referring to.
- Scale-based semantic representations
- Besides "adding detail" as a user shrinks and gets closer to what
they want to look at, the representation may change
(solid→molecule→atom→particle). The hard part would
be to see relationships between objects at different scales/
representations.
- imposters
- at a distance, a much cruder representation of an avatar works fine
Designing Interactive Collaborative Environments
Discussion from experts including the "DIVE" folks, one of the most
prominent CVE research groups, from Sweden.
WWW3D and Web Planetarium
WWW3D and Web planetarium are an example of "abstract" CVE in which 3D-ness
is not based on ordinary "world" geometry. Primary goal: aid navigation by
viewing multiple sites of interest. Each Webpage = 1 sphere.
Manual and automatic 3D layouts of these spheres.
Big problem with scalability (too many pages to view). Cluster pages
together into 1 sphere per site.
WWW3D shows very little page contents, mainly shows links; color codes
pages by how recently they were viewed. Web planetarium
uses the first image in the page as a texture (often a logo or person).
From public demo: users avoided following links to "warp" new sites into
the 3D layout. They preferred to wander around a landscape that is already
created.
The Blob
"When something is truly engaging, it can take on a life of its own and
be appropriated for applications beyond its original intended application."
- mouse gestures (stroking, pulling, etc)
- "Local Tools" approach, not "menu and palette" approach. Each item
may have several state attributes, and remembers them when set down.
- Forcing collaboration example: both users have to click an item in
order to get/select it. But, its better to "encourage" than to "enforce"
collaboration.
Robot-Human collaboration
Is mixed/augmented reality an application of CVE's?
- "deictic references", e.g. "this object here".
- Robot semi-autonomous; this is a collaboration tool not a
remote control tool.
- division of labor between robot and human is fluid
- sometimes, too many details in the model is bad, focus detail around
the job at hand.
- Pfinder, MIT Media Lab, extracts 2D+ information about people from a
video feed (tracks hands, head, center)
- Path objects, which can be illuminated or autofollowed.
- PDA 2D map as aid to 3D navigation?
Trouble in CVE-Land
- configure script bogus? It had control-M's on it that caused some
shells to choke (e.g. fedora core 3) but not others (updated)
- server connects but doesn't answer - a new kind of hang (fixed by
killing and restarting server; needs investigation)
- server won't start at all? check if someone else owns the port
their zombie iconx = you must use a different port
- can't run /sbin/lspci? Unicon version issue?
Please Read the above paper.
- EVL at UIC
- Their focus: "high quality collaboration" with few (< 8) users on
powerful computers when bandwidth is plentiful.
- CAVE: an immersive 10x10x10 VR room using projection on walls.
- 3D shutter glasses, not heavy goggles; shutter glasses also report user's
head position to VR system.
- 3 button "wand" in place of mouse; wand reports user's hand position
to VR system
- High end silicon graphics workstations to drive displays
- Applications in C/C++ and OpenGL/Performer
- Several people can go in the same CAVE, but only one controls Eye
position and carries the Wand.
- Tele-immersion = CVE's using VR hardware
- Capture and transfer gestures, audio, and video among collaborators?
At least: CG versions of collaborators which show head and hand position.
adequate for tasks, no good for negotiation.
- CALVIN - used CAVE to do multiuser architectural design.
"mortal" and "deity"
modes. "deity" mode sees the big picture, simcity-style, and good for
gross manipulations; "mortal" mode better at fine manipulations.
- NICE - a virtual environment for young children; main activity is
gardening.
- Non-humanoid avatars (e.g. bunnies); mouse-mode; ability of one avatar to
pick up and carry another avatar.
- Persistent server; changes happen even when humans are not logged in.
- Giving a newbie a flower to welcome them to the environment
- Using the "hokey pokey" to test network quality
- Avatar nametags to tell which user is from where
- Virtual webcam for world monitoring
- Applications: car designers, meteorology, oceanography, medical education,
history, virtual tourism,
- NICE suffered from lack of a clear goal; Round Earth (a successor to NICE)
had a specific goal of teaching kids that the earth is round (woohoo); one
kid sees the surface of a planet as an astronaut; the other sees the whole
ball and plays the role of "mission control".
Lessons:
- having only one hand is a liability
- other remote users' avatars can block your view! \ignore command may want
to do more than block chat.
- Long "beam" pointers are handy when pointing at distant objects
- Users will want to know what they look like to others.
- "Audio is the most important communication channel to maintain the
shared collaborative space".
- Avatars need (at least crude) mouth movements corresponding to audio,
so folks can see who is talking.
- People feel very bad when they accidentally walk through someone else;
collision detection between avatars is important. However, when
situations get crowded, the same collision detection can be a pain,
so perhaps it needs to be dynamic. A body in motion is less of an
obstacle than one at rest?
- Americans tend to gesture more than some cultures; gesturs inferred from
real world (i.e. hardware VR) need exaggerating to be clear in the CVE.
- Newbies generally require a tourguide.
- Bad jitter is worse than bad latency
Future Work for the CAVE Folks
- Asynchronous work support in the CVE requires more study.
- "Vmail" - recording of an avatar talking and referring to objects
in the virtual space. Recording most naturally belongs to the
networking layer.
- Virtual sticky notes.
What I learned from this year's Halloween
This year I went to a Halloween house of extraordinary magnitude, in which
mounted next to the front door there was a Shrek Style magic mirror. The
human operating from behind the mirror would chat with kids who were trick
or treating. In their version, the human operated a push button in sync
with his talking in order to make the magic mirror's mouth (which was just
a black diamond that opens and closes). With a little practice, it looked
fairly convincing. My thought: avatar's mouths can be automatically driven
by audio amplitudes and/or chat command text.
Managing Dynamic Shared State (SZ Chapter 5)
Goal: users see the same thing at the same time.
Key consideration: keep a consistent view of dynamic shared state.
Minimum: user position and direction
Maximum: entire world dynamic and may need to be updated
consistency vs. throughput
"by the time Joe's location arrives at mary's machine, it is already obsolete"
it is impossible to allow dynamic shared state to change frequently and guarantee
that all hosts simultaneously access identical versions of that state"
shared repositories
If the server owns the dynamic state, and clients merely request changes to
it, then all clients can be kept consistent...at a high price. The simplest
version maintains state in regular files, and omits a server entirely, using
NFS or a similar filesystem to make the state available to clients. Slow.
Limited users. Version 2 might be a SQL database, which would probably
scale better than simple files and NFS. Each operation would not involve
opening files, which is expensive. Version 3 would be: use a server, and
leave entire dynamic state in its main memory. (Q: is our current CVE using
this model?) Issues: server crash may lose state unless it gets written on
each update. TCP hogs resources, loses connections, limits maximum # of
users.
Variation: distributed shared repository, in which different dynamic state
is managed on different machines. "Virtual" centralized repository.
Idea: how consistent your information about others is can be proportional to
their importance to you or their proximity to you; this doesn't have to be a
boolean visible/too-far-away condition. What about updating with frequency
proportional to distance? Server could compute: should I send user X's move
to user Y? with probability = (1.0 - distance) * (1.0 - direction)
frequent broadcast
"blind broadcast": possibly even if the state hasn't changed? allows faster
(lower latency), unreliable protocol, since lost packets will be replaced.
More updates per second than shared database systems. system is potentially
serverless. bad part: sucks bandwidth, limits # of such dynamic objects.
Specific machine "owns" the object for which it broadcasts updates; more
complicated for others to modify the state of that object. Works best in
a LAN setting (many early LAN games used this model).
Concept: "lock lease" = locks that automatically timeout.
Latencies of 250ms are not uncommon on WANs.
Jitter = variation in latency from one packet to the next.
state prediction (dead reckoning)
idea: transmit updates less frequently, use current information to predict/
approximate future states. transmit not just (x,y) but (x,y,vx,vy) with
velocities to use until the next packet. sacrifice accuracy to support
more participants and/or run on lower bandwidth connections; decouple frame
rate from network packet/update rate. Requires surplus CPU be available.
prediction = how we calculate current state based on previous packets.
commonly using derivative polynomials (velocity, acceleration, and
possibly "jerk"). order 0 = state regeneration technique. order 1
adds velocity. order 2 (with acceleration) is "the most popular in use today".
Note: if acceleration is changing each packet, using it generates a lot of errors.
Good to disable acceleration dynamically when it is not helping, maybe use it
when it is nonzero and consistent for 3+ updates in a row.
derivative polynomials don't take into account our knowledge about the
semantics of the object. Separate dead reckoning for each class of
virtual object?
convergence = how we correct error. instead of "jumping" to correct,
we might smoothly adjust. goal = "correct quickly without noticable
visual distortion". "snap convergence" just lives with the distortion.
linear convergence: given the corrected coordinates, predict where
the object will be in 1 second. Now, compute the prediction values
for the object so that it moves from its current, erring position to
where it is supposed to be a second from now. (what if this runs
through a wall?)
To do better: use a curve-fitting algorithm, maybe a cubic spline.
Reflections on a MOO scenario
- out of curiosity, thinks Dr. J, how usable/useful would our CVE
be if we supported non-3D operation?
- commands "look" and "look becki". Do we need them?
("look" describes the room you are in. "look user" describes someone).
- "blink": a good name for an "I am not afk" command.
- "idle driving into the lab": tells others why you are afk and maybe
gives them an idea of for how long.
- "wave", "nod", "laugh" etc.
- "paging": sending a message from another room; is this term more
evocative than "tell"?
- advantages of MOO-mode: requires little CPU, unobtrusive,...
- major feature: avatar sits there listening to all conversation
in a room even when you are not there, all you have to do is
scroll backwards to review it (extra client commands to help with this)
- "persistence underlies the development of a notion of place - and
places can make people feel like they belong and want to go back"
- "MOO room" = "project" or "group" area for chat. User-creatable.
- multiple avatars, having an avatar in a room means one is recording
what's happening for later review.
- alert = key < 1 minute; daydreaming = key < 5 minutes;
idle = afk < 1 hour;
staring into space = away > 1 hour
- summaries during your absence: help you know whether to scroll back
and look at the logs, and if so, what (or who) you are looking for.
- MOO downsides: whispers promote secretiveness; potential for
disastrous typing mistakes; rooms promote and make conspicuous the
social cliques, and perhaps make them subject to scrutiny.
- "more effort has gone into the creation and support of persistence
mechanisms than any other type of development".
Reading Assignments
- [Zyda 2005] IEEE Computer September 2005, "From Visual
Simulation to Virtual Reality to Games", Michael Zyda
Google for the title.
- the TOP paper
All About Display Lists
2D windows can remember their contents in case they need to be redrawn by
keeping an in-memory copy of the entire window, a so-called "backing store".
In the case of 3D windows, we might use a similar strategy but instead
keep a display list, which is a data structure that contains
all the data about all graphics operations that have been performed
since the last time the 3D window was opened or erased.
OpenGL has a display list concept, but its display lists would not be
easily manipulated from the Unicon application level, so we maintain
our own display list as a regular Icon/Unicon list. Each element of
the list is a list or record produced as a by-product of a 3D output
primitive (either a 3D function call, or an attribute that was set)
written on that window. Unfortunately, the elements of the
display lists are somewhat underdocumented at presents, so we will
describe them in detail here.
Display Lists blindfolded
By brute force you can see what's in a display list as follows:
L := WindowContents(w)
every i := 1 to *L do {
writes(i, ": ", image(L[i]), " -> ")
every j := 1 to *(L[i]) do writes(image(L[i, j]), " ")
write
}
Display Lists with the headlights on
Each element of the display list is either a list or a record.
The first item in the list or record is the name of the 3D
primitive, and the remaining items are the parameters that were
passed in when that call was made. Whenever Refresh(w) is called,
or whenever the window system requests a repaint, the Unicon VM
walks through the display list and repeats each output operation
from the list. The following table summarizes the display list
elements. Type is either a list with the contents as described, or
it is the built-in record type indicated.
3D Function | type | notes
|
---|
| gl_torus(name, x, y, z, radius1, radius2) |
|
| gl_cube(name, x, y, z, length) |
|
| gl_sphere(name, x, y, z, radius) |
|
| gl_cylinder(name, x, y, z, height, radius1, radius2) |
|
| gl_disk(name, x, y, z, radius1, radius2, angle1, angle2) |
|
| gl_rotate(name, x, y, z, angle) |
|
| gl_translate(name, x, y, z) |
|
| gl_scale(name, x, y, z) |
|
PushMatrix | gl_pushmatrix(name) |
|
| gl_popmatrix(name) |
|
| gl_identity(name) |
|
| gl_matrixmode(name, mode) |
|
Texture | gl_texture(name, texture_handle:integer) | internal code used by OpenGL
|
Fg | ["Fg", ["material", r, g, b], ... ]
|
Attribute settings get put on the display list as well.
Attribute | type | notes
|
---|
linewidth | ["linewidth", width]
|
dim | ["dim", i]
|
texmode | ["texmode", i]
|
Texcoord | ["Texcoord", val]
|
Flaws in the current Display List abstraction
Yes, there are some flaws. Does the display list understand
graphics contexts? It feels like a single-context model to me.
Extending Texture(w, x) to Texture(w, x1, x2)
Problem: you can create new textures, but how do you free/reclaim old ones?
We will run out of texture memory sooner or later, but it needs to be later.
tex := Texture(w, s)
...
Texture(w, s, tex)
Will modify an existing texture on the display list, instead of creating
a new one. It will also set the current texture to tex.
How soon? Well, I put the prototype code into ~jeffery/unicon/unicon
last night, but it isn't tested or checked out on Windows yet. I'll try
for ASAP.
Working in AlphaWorld (Churchhill chapter 15)
Alphaworld is a
$6.95/mo, primarily social CVE. But here (Chapter 15) is a strong
endorsement for it.
- Goal: use a off-the-shelf CVE for work collaborations instead of
pure social.
- AlphaWorld vs. Blaxxun: AW less flexible, less visually appealing,
but Blaxxun (VRML based) had problems: special/weird browser, poor
performance... AW was a much smaller/faster browser.
- Reason voice chat is so important is so you can talk and "work"
(i.e. type) at the same time. With text chat you have a major
task- and window- switching issue.
- A Major feature of AW: extensibility
- Participatory Design: users and designers (of content)
are one and the same. Who gets write permission? How easy
is it to report a bug or request a change?
Lessons from Blaxxun
- spatial properties not used after initial exploration
- objects were not designed/built by users, had nothing to do with
their topics of interest. No reason to interact with the world.
- lack of access to documents, no screen space available for other
windows, led to a deficiency of content needed for collaborations.
- lack of privacy
- passers-by would hurl verbal abuse during business meetings!
Lessons from AW
- Sheer size allows for privacy: uninvited do not just happen by
- land-claim metaphor; you stake out 2D land before building in 3D on it
- Build tool integrated into regular viewing client, not a separate tool.
- Building your own space is a huge value-add for collaboration,
mainly if you can populate it with useful content, e.g. URL's.
- AW was used in conjunction with BSCW.
- Easily implementable 3 grades of accessibility a la UNIX (ugo)
- Using stable public technology makes a project open -- they
don't have to maintain the servers, accounts, etc.
- Individual ownership of objects was central in text-based MOO's
but lost/dropped in many 3D CVE's.
- Build-by-copying. You can't create new objects, but you can
copy and then modify existing objects.
- Clicking on or bumping into 3D objects "scriptable" with user-defined
response (e.g. pulling up a webpage).
- Informal "chance encounters" are not chance at all, they are people
we half-expect to meet, and benefit from meeting occasionally.
- AlphaWorld-based document navigation better than BSCW text interface;
3D navigation benefits scalability of file system access?!
- Creation of memory of the space came from using the space daily.
- Ease with which users can add their own rooms is crucial to a CVE's
success
- Frontier Hypothesis - plentiful land waiting to be settled
- "New World Times" - server problems? A "giant meteor crashed into
the virtual world, destroying everything..."
- Dr. J idea: give users disk space proportional to time spent in the CVE
for customization.
- Value of the CVE to your friends depends on your being there a lot.
Resource Management
Physical View of Resources
Resources: CPU + GPU + Net + disk + main memory
Singhal/Zyda assert that it is the Net resources that comprises the
"cornerstone" of Networked Virtual Environments. All of the resource
management in a NVE can be viewed in an information-centric way. The
resource utilization for any category is: how much information is that
resource using/processing.
Network-Centric View
Resources = M x H x B x T x P, where
M = Messages,
H = Hosts (destinations/message),
B = Bandwidth (bytes/message),
T = Timeliness (1/latency),
P = Processor (cycles/message)
Typically to reduce the usage of one of these resources, you increase the
use of other(s), usually using more Processor power to reduce others.
Resource Tradeoff Techniques
- Compression
- lossless vs lossy, internal vs external
- Packet aggregation
- Can reduce bandwidth up to 50% at a cost of worse timeliness.
Compare timeout vs. quorum vs. hybrid approaches to packet aggregation.
Aggregation servers: might be organized by physical lan, msg type, teams, region
- Visibility of Data
- Area of interest filters, subscription-based, multicasting. Aura-nimbus approach
allegedly performed poorly in MASSIVE
- Level of Detail
- Far away = small inaccuracies invisible
- Server Clusters
- CPU bottleneck or network bottleneck?
client (group) partitioning, or region/zone partitioning
A Component Framework
Today: a couple of articles from the CVE 2002 conference. Some of the
future lectures will be from "Inhabited Information Spaces", a 2004
Springer book that was produced by the people who did our CVE textbook.
This paper is from the proceedings of the CVE 2002 conference. It is OK to
skim this paper.
- From the "America's Army" game team at NPS
- NPSNET-V, fifth generation multiuser network game architecture?
- Java/XML, XML mainly for serialization that resists version conflicts
- upgrade components at runtime, never bring system down
- everything is upgradable except for a small "microkernel"
- strict hierarchical containment resembling file system directories
- "most important pattern" = Model-View-Controller
- Other projects: Bamboo (VR98, multilanguage multiplatform pluggable VE),
JADE (Java Adaptive Dynamic Environment),
- replace() and retire() methods: cooperative handshake between old and
new versions of a component
A bit more on Model-View-Controller
Originated in SmallTalk; often considered a "design pattern".
Idea: separate complex components into: three classes
- Model: contains the state
- View: depicts the state to the user
- Controller: modifies the state
Reasons: allows multiple views/controllers; localizes changes.
Downsides: three mirrored class graphs, instead of one; the "model"
class is potentially all representation w/little behavior
Cursive: Controlling Avatar Gesture Using Pen Gesture
This paper is from the proceedings of the CVE 2002 conference. You have
to read this paper (but it is short).
- (handwritten) mouse/pen gesture translated into avatar gesture
- separate symbolic and expressive parts of gesture
- user picks from a fixed set of gestures (symbols), but gets to
convey expressiveness, e.g. via speed, size, or slant, of character
(or if you had a tablet device, the pressure of the pen on it).
- How easy would it be to do a trivial version of this? What about
other, lower-level gesture mechanisms? How many avatar gestures
would be useful in our app?
Some random thoughts on a low-end cursive-like gesture system follow:
- use fairly small patch of window in an "avatar area"
- base purely on press-drag*-release sequences in that area
- "size" based on pythagoras equation on the bounding box
- "speed" based on sum of &interval times (could use sequence of &intervals)
- want a few gestures, without a full handwriting recognition package
- compare w/ Palm pilot Graffiti: a simplified symbol set
- raise hand, lower hand, smile, shrug, etc.
In class, a lively discussion of the use of keyboard-driven emotes
ensued. Switching between keyboard and mouse was seen as undesirable
under many circumstances.
Keyboard emotes could capture rough sizes (e.g. the number
of times you repeat !!!!) but not as many timing nuances. Also
discussed was the idea of using a toolbar/palette/menu to select
the symbol, and then emoting need only measure "expressiveness".
Opening Comic
Things Our CVE Needs "Real Bad"
- more server state; server keeping track, e.g. of door states,
blackboard states, user states; backing this up on disk
- network commands that provide
ability to transmit (efficiently) blackboard contents and changes
to server and thence to interested clients.
In Class n-User CVE Demo
- The server is too fragile. The VM code to handle lost connections
appears to have a bug. In addition the VM on the client
should probably be extended to close network connections on program
termination, apparently just calling exit() is not very friendly.
- Some avatars' heads look much better than others (Gustav and Yo,
for example. The difference is not original resolution in pixels,
but rather, a solid background allowed more colors for the actual face.
Adding JPEG support for textures, or using a 24-bit BMP format,
would fix this without having to edit/redo the images manually.
- Performance - I couldn't tell whether the network or the server
limited client performance; we won't know until the clients'
performance and user interface have been tuned. It was OK with 5.
Q: When is HTTP the right protocol for file transfers?
A: If/when it saves us any work to get our CVE prototyped.
Q: Why and for what, do we need file transfers?
A: User-supplied textures. New data and code files. Patching the
executable.
Features We Need to Integrate
- blackboard/whiteboard
- configuration files (config GUI screen)
- map window
- groups
- accounts/passwords
- user state
- VOICE - www.cs.nmsu.edu
- texture objects
- API support for manipulating the display list
- API support for manipulating the event pending list
Features We Need to do Starting From Scratch
- patcher
- file transfer
- corridors
- document sharing
- MIME types - fire off external programs
- transmit avatars @ runtime
Features we Wish we Could Do, But not Anytime Soon
- room builder/ room decorator
- ghost (or multi) avatars
- much better avatars
- Icons for education - assignments, labs, exams, discussions
Links of Interest
Miscellany on other Genres
RPG's
Strategy games are typically third person. You are not on the board and
although you are the general, your relationships with your troops are
pretty distant.
Role Playing is some stupid exercise used by psychologists and counselors
to try to get people to see situations from different points of view. But
Role Playing Games starting with Dungeons and Dragons applied that basic
technique to imaginative first-person gaming.
Example Pencil-and-Paper RPG's
- Dungeons and Dragons, Tunnels and Trolls, Bunnies and Burrows...
- Micro-RPG's (Melee, Wizard)
- Traveller (SCI-FI), and Shadowrun (Cyberpunk)
- Vampire the Masquerade, and other monster-genres
Example PC-based RPG's
Often these are attempts at straight computer implementation of a
manual game
- Rogue, hack, nethack, moria
- Diablo
- Icewind Dale, Neverwinter Nights
Educational Games and Serious Games
Education is a major area for games.