Visual Pinball, creating a table of your own.


This helpfile is here to help you on your way. Easy clicking without adds or pop-ups.
I find it usefull to have a place where I can grab some code and copy-paste it into the script.
After a while, it's enough to look at the code to get the answer to your question.
In my own tables, I found that an important part of the script is there to make sure that
unwanted things do not happen, mostly because of multi-ball.
If you're new to Visual Pinball, start with creating a simple test-table without multi-ball.
That way, you don't have to start with thinking what could happen if multiple targets
are hit at the same time, or 1 target is hit by multiple balls.
It's also a good idea to take a new table and place all objects and klick all buttons.
Just look at the options you can set at each object. Also change the table-options and see what
happens. Delete the table when you're done testing.
The table dimensions are best set before you start with building the table.

These are just tips, not rules. Things that could help make your table better.
I'm no expert, I just like pinball. My tables are mostly made for my own pleasure.
It's nice to see others enjoying them as well.
There are loads of people out there who build way better tables than me.


back to index         -         next page

- General Building Tips
- Key Code for Table Control
- Errors



Less is More !!!

Try to keep the script as short as possible.
If you find a way to save only a few lines, change it, it's worth it.

Only calculate things when it's realy needed.
A lot of calculating decreases performance.
Try not to use divide. Somehow score/2 takes more processingpower than score*0.5 .

When shaping walls in the table edit-mode, keep in mind that curves take a lot more processingpower
than straight edges. Also, a lot of small walls can drasticly decrease performance.
Loads of walls are there just for the looks, like small pins or big objects.
It doen't matter if the ball can go through them, because the ball will never get there.
If that's the case, uncheck the Collidable box in the wall's options part.
This way, the computer doesn't calculate it's edges for ball collission.
If your table runs slow or seems to stutter, you may have to decrease the number of walls an/or
decrease the number of curved walls and corners.
If you can't see the wall, like with caves, uses straight lines and as little corners as possible.
Doing this from the start, will save you a lot of pain.

Switching off the shadows in the table-options can improve performance.
It also makes the table more visible, because nothing gets darkened by shadow.
Test it and see what you prefere most.





General Building Tips

Just some things that work for me, or things I found during programming.
If you understand how the code works, these tips will make sense.

Names:
All names of all objects have to be unique, otherwise VP changes the name by itself.
If you want 3 lights to light up, you have to use 3 commands, or 1 sub-routine with 3 commands.
You can't give 3 different lights the same name, this isn't possible.

Timers:
Whenever a command should be executed after a given amount of time, a timer is used.
You will find yourself using them a lot.
Try to use the build-in timers of objects as much as possible.
These timers can be used in any sub-routine and are not specificly used by the object itself.
It's the code you write that tells what happens if a timer is activated.
[] Timer Enabled : ALWAYS KEEP THIS UNCHECKED AND OFF !!!,
This activates the timer at the start of the game
Object.TimerEnabled=True can be used to start the timer when you want it to start.

Kickers:
When a ball hits a kicker, it stays there untill the command Kicker.DestroyBall or
Kicker.Kick 180, 8 is given.
Put this command at the end of the hit-routine. Keep the ball in the kicker untill the end.
This way, another ball can't hit the kicker, preventing any mistakes from happening in the script.
This is a very helpfull trick in multi-ball tables.
Make sure you use it on the drain also, wich is actualy a kicker named drain.

Table Height:
A nice table has different heights with paths the ball can travel.
Ramps can be a great help in creating height.
A high tower with ball release and a ramp attached, takes little space, but looks nice.
I believe a ball needs about 60 height to roll. I mostly use 90 as bottomheight an 100 as topheight
for the fist layer, making it 10 units thick. This gives the ball some space to bounce.
If the opening between the floor and a wall is somthing like 30 units, the ball can get stuck under it.
Just experiment with what looks good enough and works well.
Sometimes a ball hits a ramp so hard, that it's launched to a place where it shouldn't be.
This happens in real pinball tables all the time. In Visual Pinball, it's easy to fix.
If it happens only on a rare ocasion, just add a trigger in the place the ball lands and transport
it to another place where it's released. The code for this could be something like:
Sub WrongPlace1_Hit
        WrongPlace1.DestroyBall
        WrongKicker.CreateBall
        Wrongkicker.Kick 180, 5
End Sub

Moving Objects:
A nice table has movement in it. Enough dropping targets and something special.
You can create nice visual effects with dropping targets that have a Flipbook Animation.
When dropped, they won't be visible. When up, they are visible.
Decals didn't work for me, so I made a 2nd image as large as the background, used the blueprint
to see where the target was and made the graphics, using the image as top image of the target.

Using Lights:
Lights make the table shine, but they can also be used to keep track of things.
Use the state of a light in a sub-routine as indicator that something has happened.
Let's say a light is named ShootExtraBall. Only when this light is blinking, the target gives
an extra ball. In the hit-routine it looks like:
If ShootExtraBall.State=2 Then
PlaySound "ExtraBall"
ExtraBallCount=ExtraBallCount+1
ShootExtraBall.State=0
End If
This way, you don't need a Dim to keep track of things.
And these sort of lights make a table look better. You can shape them into anything.
You can place a decal over the light to make a text or image light up.

Keep the ball on the roll:
When playtesting, you will find that the ball rolls to certain parts of the table.
Try to find these and build your lanes and ramps there. There's no fun in missing all the time.
Also try to avoid the ball going to the same flipper each time it comes down.
A good table with nice ball-paths is fun to play-test, even if there's not much on the table yet.

Watch the lane width:
I use a kicker as minimum spacing between walls, place it to check the width and delete it.
You can fine-tune after playtesting.
Ramps can be any height-width. I stick to a minimum width of 60.

Big Events & Play-Testing:
Some events take a long time to activate when playtesting. Just add the code under a key in the
key-routine of the table (at key-up). Perhaps you need to write a temporary sub-routine to
start and test the event properly. This saves a lot of time and helps you find errors faster.


Go to Menu



Needed Code: Key Code for Table Control

The Key-Routines make the table react to the player.
When starting a new table, this is already in the script. Simply change it when needed.
You can also use the keys to test a piece of code.
Copy and paste the sub-routine to the key routine under a specific key.
To find a complete list of keynumbers, just search the web.
The key-routines are just another sub-routine, and can be modified if desired.
KeyDown is activated when a key is pressed.
KeyUp is what happens when a key is released.
Table1 is the standard name of a new table. If you change it, you need to change the script also.

Sub Table1_KeyDown(ByVal keycode)

If keycode = PlungerKey Then      
the standard plungerkey is Return/Enter.
Plunger.PullBack
End If

If keycode = LeftFlipperKey Then      
the standard leftflipperkey is LeftShift.
LeftFlipper.RotateToEnd
PlaySound "FlipperUp"
End If

If keycode = RightFlipperKey Then      
the standard rightflipperkey is RightShift.
RightFlipper.RotateToEnd
PlaySound "FlipperUp"
End If

If keycode = LeftTiltKey Then
Nudge 90, 2
End If

If keycode = RightTiltKey Then
Nudge 270, 2
End If

If keycode = CenterTiltKey Then
Nudge 0, 2
End If

End Sub


Sub Table1_KeyUp(ByVal keycode)

If keycode = PlungerKey Then
Plunger.Fire
End If

If keycode = LeftFlipperKey Then
LeftFlipper.RotateToStart
PlaySound "FlipperDown"
End If

If keycode = RightFlipperKey Then
RightFlipper.RotateToStart
PlaySound "FlipperDown"
End If

If keycode = 19 Then      
this is a test routine, activated with "R".
HQKicker.CreateBall
HQKicker.Kick 90, 5
BallInPlay=BallInPlay+1
End If

If keycode = 21 Then      
this is a test routine, activated with "Y".
Randomize
MysteryWin = Int(Rnd * 10) + 1
MysteryTwee()
End If

End Sub



Go to Menu



Errors

You wil find a lot of errors in your table/script when playtesting.
It's important to get all errors out of your table, a game isn't fun if it has to be reset.
A lot of time is spend play-testing the table to see if all works well.

Names:
All names of all objects have to be unique, otherwise VP changes the name by itself.
If you want 3 lights to light up, you have to use 3 commands, or 1 sub-routine with 3 commands.
You can't give 3 different lights the same name, this isn't possible.

Ball launch:
Sometimes a ball hits a ramp so hard, that it's launched to a place where it shouldn't be.
This happens in real pinball tables all the time. In Visual Pinball, it's easy to fix.
If it happens only on a rare ocasion, just add a trigger in the place the ball lands and transport
it to another place where it's released. The code for this could be something like:
Sub WrongPlace1_Hit
        WrongPlace1.DestroyBall
        WrongKicker.CreateBall
        Wrongkicker.Kick 180, 5
End Sub
If transporting the ball isn't an option because it happens to often, change that part of the table.
In case the object just doesn't seem to do what you want, delete it and build something else.

Script Errors:
Writing (programming) the script is very unforgiving.
Forgetting one letter or things like " or . can screw up the whole script.
Not all errors are spotted when starting the table. Playtest everything for errors.

Most common scripterrors are forgetting End If or End Sub.
The program will give a notification like: "line 212: Excpected End of Statement"

Another common error is naming errors. A name has to be typed exactly for the routine to work.
If you have added something new, check if the name of the new object is the same as in the script.
The program will give a notification like: "line 387: No Such Object"
If you hit a kicker/trigger/object in the table, and nothing happens, it could be a naming problem.

A routine runs from top to bottom. If the wrong thing keeps happening when a target is hit,
it may be that you need to relocate certain commands in the routine, moving them up or down.

Build Errors:
If a ball gets stuck, check the heights of all walls.
If the top of a wall (as seen in the editor) is to flat, the ball won't roll down.
When 2 walls are to close together, the ball will get stuck.
The physical wall of a ramp can cause problems if other objects are above the ramp.

If objects seem to be missing, check if they are on the right height in the table (surface).

If objects don't seem to work, check the infobox if all options and checkboxes are set correctly.

If things just seem to happen spontaniously at the start, check the infobox of used timers or objects
with timers to see if []timer enabled is still switched on somewhere, this should be off.

Multi-Ball Errors:
In the beginning, you will have to watch the BallInPlay count. If this doesn't work right,
the table won't reset or resets to early. Check and change the code.

Most errors with multi-ball come from sub-routines running twice or timers still enabled when
another ball hits and the routine calls upon the same timer.
It takes some time to tune a routine for multi-ball.
When a ball hit's a kicker and is destroyed there to be created somewhere else, then first
create the new ball and release it before destroying the ball in the kicker at the end of the routine.
When a ball hits a kicker, it stays there untill the command Kicker.DestroyBall is given.
Put this command at the end of the routine. This keeps the ball in the kicker untill the end.
This way, another ball can't hit the kicker, preventing any mistakes from happening in the script.
Make sure you use it on the drain also, wich is actualy a kicker named drain.
(Most of the time, the Kicker.DestroyBall is placed at the end of the timer-routine)

Some hit-routines can start something when there's just 1 ball in play and only give points/bonus
when there are more balls in play. Just place the following code at the start of the routine.
In this example, in the hit-routine of ItemSelectKicker (BallInPlay is a Dim)
Sub ItemSelectKicker_Hit()
If BallInPlay>1 Then
AddScore(1000)
AddBonus(50)
ItemSelectKicker.TimerEnabled=True
Exit Sub
End If
...the rest of the routine...
End Sub

Sub ItemSelectKicker_Timer()
ItemSelectKicker.TimerEnabled=False
ItemSelectKicker.Kick 0, 35
End Sub






Go to Menu