Dagens ord om Skeptikerpodden

John Houdi säger det så bra, så jag låter han tala:

Tänk vad fort det går. Vi har nu gjort 16 avsnitt av Skeptikerpodden!

Med tanke på den respons vi fått så har denna poddradio varit eftertraktad länge, vilket vi som gör Skeptikerpodden tycker är oerhört kul.

Läs inlägget här.

First screenshot of the SRT tool

Just felt the urge to publish this first screenshot of my new SRT tool. I aim to make this the easiest way to synchronize a subtitle file (*.srt) with a DivX movie.

Writing a game using TurboSprite (2/2)

Continued from here.

Now I want to enable shooting. When the user presses the Control key, I create a bullet, sets its properties and adds it to the engine. This code is added to the KeyDown event. I have to modify the KeyUp event, because in the previous version, it unconditionally made the ship stop. Now when more keys are involved, I only want the ship to stop if it is the up or down buttons are released.

This is the new version of the KeyDown event:

   Private Sub Form1_KeyDown(ByVal _
sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      If e.KeyData = Keys.A Then

         'If user presses "a", move up!
         Me.PlayerMover.DestY = 0
         Me.PlayerMover.SpeedY = -2

      ElseIf e.KeyData = Keys.Z Then

         'If user presses "z", move down!
         Me.PlayerMover.DestY = SpriteSurface1.Height
         Me.PlayerMover.SpeedY = 2

      End If

      If (e.KeyData And Keys.ControlKey) = Keys.ControlKey Then

         'If user presses Control, fire!
         Dim Bullet As New SCG.TurboSprite.PolygonSprite(2, 0, -2, 1, -2, -1)
         Bullet.Position = New Point(Player.Position.X + 15, Player.Position.Y + 9)
         Bullet.Color = Color.Cyan
         SpriteEngineDestination1.AddSprite(Bullet)
         Dim BulletMover As SCG.TurboSprite.DestinationMover = _
SpriteEngineDestination1.GetMover(Bullet)
         BulletMover.Destination = New Point(SpriteSurface1.Width + 5, _
Bullet.Height)
         BulletMover.SpeedX = 4

      End If
   End Sub

This is the new version of the KeyUp event:

   Private Sub Form1_KeyUp(ByVal sender As _
Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp

      'Stop moving, but only if "a" or "z" is released.
      If e.KeyData = Keys.A Or e.KeyCode = Keys.Z Then
         Me.PlayerMover.SpeedY = 0
      End If

   End Sub

Finally, when a bullet reaches its destination at the right end of the screen, I want it deleted. Check the SpriteReachedDestination event in the final code listing below.

When this is all working, I want to add an enemy and use the collision detection to see if it was killed. I did not subclass the PolygonSprite, and this is really something you should do to be able to tag the sprites. The “ugly hack” of this post, must be that I use color to determine if the sprite is a bullet, or the enemy. This is the complete code:

Public Class Form1

   'A reference to the player (created in the Shown event) and the mover.
   Private Player As SCG.TurboSprite.PolygonSprite
   Private PlayerMover As SCG.TurboSprite.DestinationMover

   Private Sub Form1_Shown(ByVal sender _
As Object, ByVal e As System.EventArgs) Handles Me.Shown

      'Set the desired number of frames per second and activate the surface.
      SpriteSurface1.DesiredFPS = 40
      SpriteSurface1.Active = True

      'Create the player and save the reference.
      Me.Player = New SCG.TurboSprite.PolygonSprite(-20, -10, 20, 10, -20, 10)

      'Set player original position.
      Me.Player.Position = New Point(100, 100)

      'Set player color.
      Me.Player.Color = Color.Yellow

      'Add the player to the sprite engine.
      SpriteEngineDestination1.AddSprite(Me.Player)

      'Save a reference to the mover.
      Me.PlayerMover = SpriteEngineDestination1.GetMover(Me.Player)

      'Now, add an ENEMY!
      Dim Enemy As New SCG.TurboSprite.PolygonSprite(-30, 20, 0, -20, 30, 20)
      Enemy.Position = New Point(SpriteSurface1.Width, SpriteSurface1.Height)
      Enemy.Color = Color.Red
      SpriteEngineDestination1.AddSprite(Enemy)
      Dim EnenyMover As SCG.TurboSprite.DestinationMover = _
SpriteEngineDestination1.GetMover(Enemy)
      EnenyMover.Speed = 1
      EnenyMover.Destination = New Point(-50, 100)

   End Sub

   Private Sub Form1_KeyDown(ByVal sender As _
Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      If e.KeyData = Keys.A Then

         'If user presses "a", move up!
         Me.PlayerMover.DestY = 0
         Me.PlayerMover.SpeedY = -2

      ElseIf e.KeyData = Keys.Z Then

         'If user presses "z", move down!
         Me.PlayerMover.DestY = SpriteSurface1.Height
         Me.PlayerMover.SpeedY = 2

      End If

      If (e.KeyData And Keys.ControlKey) = Keys.ControlKey Then

         'If user presses Control, fire!
         Dim Bullet As New SCG.TurboSprite.PolygonSprite(2, 0, -2, 1, -2, -1)
         Bullet.Position = New Point(Player.Position.X + 15, Player.Position.Y + 9)
         Bullet.Color = Color.Cyan
         SpriteEngineDestination1.AddSprite(Bullet)
         Dim BulletMover As SCG.TurboSprite.DestinationMover = _
SpriteEngineDestination1.GetMover(Bullet)
         BulletMover.Destination = New Point(SpriteSurface1.Width + 5, _
Bullet.Height)
         BulletMover.SpeedX = 4

      End If
   End Sub

   Private Sub Form1_KeyUp(ByVal sender As Object, ByVal _
e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp

      'Stop moving, but only if "a" or "z" is released.
      If e.KeyData = Keys.A Or e.KeyCode = Keys.Z Then
         Me.PlayerMover.SpeedY = 0
      End If

   End Sub

   Private Sub SpriteEngineDestination1_SpriteReachedDestination(ByVal sender As _
Object, ByVal e As SCG.TurboSprite.SpriteEventArgs) Handles SpriteEngineDestination1.SpriteReachedDestination
      'You really should subclass the bullet to be able to
      'tag it as a bullet. I use color. Hack!!!
      Dim P As SCG.TurboSprite.PolygonSprite = CType(e.Sprite, SCG.TurboSprite.PolygonSprite)
      If P.Color = Color.Cyan Then
         P.Kill()
         SpriteEngineDestination1.RemoveSprite(P)
      End If
   End Sub

   Private Sub SpriteSurface1_SpriteCollision(ByVal sender As _
Object, ByVal e As SCG.TurboSprite.SpriteCollisionEventArgs) Handles SpriteSurface1.SpriteCollision
      'Did the player shoot the enemy? Is one a bullet and one the enemy?
      'Again, I use color. Hack!!!
      Dim FirstSprite As SCG.TurboSprite.PolygonSprite = CType(e.Sprite1, _
SCG.TurboSprite.PolygonSprite)
      Dim SecondSprite As SCG.TurboSprite.PolygonSprite = CType(e.Sprite2, _
SCG.TurboSprite.PolygonSprite)
      If FirstSprite.Color = Color.Cyan Then
         'We have a bullet.
         If SecondSprite.Color = Color.Red Then
            'And the enemy.
            SecondSprite.Kill()
            SpriteEngineDestination1.RemoveSprite(SecondSprite)
         End If
      ElseIf FirstSprite.Color = Color.Red Then
         'We have the enemy.
         If SecondSprite.Color = Color.Cyan Then
            'And a bullet.
            FirstSprite.Kill()
            SpriteEngineDestination1.RemoveSprite(FirstSprite)
         End If
      End If
   End Sub

End Class

Again, subclass and add your own properties! Do not use color to determine what sprite you are dealing with. This is the result:

Thank you, TurboSprite!

En snabbkurs i homeopatiska läkemedel

En liten snabbkurs i homeopatika från Der Spiegel, tips från Orsakverkan (Twitter).

D1 betyder att lösningen är spädd med en del av den aktiva ingrediensen och 9 delar vatten. D2 betyder att en D1 lösning är spädd på samma sätt, så att en hundradel av lösningen är den aktiva ingrediensen. Siffran talar alltså om hur många nollor du ska ha i faktorn. D1 betyder 1:10. D2 betyder 1:100. D3 betyder 1:1000. D9 betyder 1:1 miljard, alltså ungefär som en droppe av den aktiva substansen i en tankbil. D78 betyder att av alla molikyler som finns i hela universum, är några stycken från den aktiva ingrediensen. Enligt Der Spiegel används lösningar upp till D1000 i Tyskland. Vågar man påstå att homeopater säljer dyrt kranvatten?

Du som inte redan följer Pekkas blogg, Orsakverkan, hittar den här.

Writing a game using TurboSprite (1/2)

There are two things to take away from this post. The ease of TurboSprite and the power and speed of GDI+. Of course, you are still supposed to write games using hardware acceleration, but you can do decent desktop games without DirectX, XNA or OpenGL.

In this first part, I will create a player (a spaceship) that can respond to keyboard control. In the second part, the player will be able to shoot and kill enemies.

To start off, I create a project, I add a sprite surface and an sprite engine destination. I connect them to each other by setting the Surface property of the engine. Also, I intend to use collision detection, so I set the DetectCollisionSelf property to True.

On the surface, I set the AutoBlank property to True and the AutoBlankColor property to Black. On the form, I set KeyPreview to True.

Note that so far, all the magic is happening in KeyDown, KeyUp (input feedback) and Shown (initialization).

Public Class Form1

   'A reference to the player (created in the Shown event) and the mover.
   Private Player As SCG.TurboSprite.PolygonSprite
   Private PlayerMover As SCG.TurboSprite.DestinationMover

   Private Sub Form1_Shown(ByVal sender As Object, ByVal e As _
System.EventArgs) Handles Me.Shown

      'Set the desired number of frames per second and activate the surface.
      SpriteSurface1.DesiredFPS = 40
      SpriteSurface1.Active = True

      'Create the player and save the reference.
      Me.Player = New SCG.TurboSprite.PolygonSprite(-20, -10, 20, 10, -20, 10)

      'Set player original position.
      Me.Player.Position = New Point(100, 100)

      'Set player color.
      Me.Player.Color = Color.Yellow

      'Add the player to the sprite engine.
      SpriteEngineDestination1.AddSprite(Me.Player)

      'Save a reference to the mover.
      Me.PlayerMover = SpriteEngineDestination1.GetMover(Me.Player)

   End Sub

   Private Sub Form1_KeyDown(ByVal sender As Object, ByVal _
e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
      If e.KeyData = Keys.A Then

         'If user presses "a", move up!
         Me.PlayerMover.DestY = 0
         Me.PlayerMover.SpeedY = -2

      ElseIf e.KeyData = Keys.Z Then

         'If user presses "z", move down!
         Me.PlayerMover.DestY = SpriteSurface1.Height
         Me.PlayerMover.SpeedY = 2

      End If
   End Sub

   Private Sub Form1_KeyUp(ByVal sender As Object, ByVal _
e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyUp

      'Stop moving!
      Me.PlayerMover.SpeedY = 0

   End Sub

End Class

Continued here.

TurboSprite

TurboSprite is a software driven (GDI+) sprite engine written by Dion Kurczek in C#. The reason that I’m picking this up, is that it shows (like I have mentioned before) the amazing capabilities and performance of the GDI+ library in .NET. TurboSprite provides a sprite container, bitmap sprites, vector sprites and collision detection.

To be able to use TurboSprite, download and compile it, add the components to your toolbox. Then, add a surface and an engine to a form. Note that you have two engines. One is called SpriteEngine and another is called SpriteEngineDestination. Use the SpriteEngineDestination object, because it extends the SpriteEngine class with the ability to move sprites. You might want to set a few properties to connect the engine to the surface, to tell the engine if you want collision detection and so on. Collision detection is a reasonable cause for using a sprite engine. I use the AutoBlank feature of the surface.

About the moving of the sprites, you can acquire the sprite mover using the GetMover function for the engine (if you use the SpriteEngineDestination), and set the X and Y speed of a sprite. Or you can do as I do in this example below. That is, just set a speed and a destination position. The destination position determines the sprite movement direction.

This code does two thinks (written in the Show event of a regular form). The first line activates the sprite engine so that sprites will move. The rest of the code initializes one vector sprite.

'When the form is loaded, activate the surface
SpriteSurface1.Active = True

'Create a space ship. These points makes a ship.
Dim S As New SCG.TurboSprite.PolygonSprite(0, -10, 5, 10, 0, 5, -5, 10)

'Tell the sprite to rotate.
S.Spin = SCG.TurboSprite.SpinType.Clockwise
S.SpinSpeed = 3

'Start position.
S.Position = New Point(30, 30)

'Add the sprite to the engine, and acquire the sprite mover from the engine.
SpriteEngineDestination1.AddSprite(S)
Dim Mover As SCG.TurboSprite.DestinationMover = SpriteEngineDestination1.GetMover(S)

'Tell the mover about speed and direction.
Mover.Speed = 5
Mover.Destination = New Point(50, 40)

'Tell the mover that the destination really isn't a destination,
'it's just used as a direction.
Mover.StopAtDestination = False

This is the sprite:

Download the engine from the Code Project web site.

User interaction: GetItemAt and GetItemsAt

GetItemAt

The GetItemAt function returns the graphical item (text item, bar item, numeric item) that exists at a given position, in characters. If no item exists at the given position, the value Nothing will be returned (null). The GetItemsAt function also takes a position (X and Y) as argument but GetItemsAt will always return a collection object. If no graphical items exist at the given position, the collection will be empty. If more than one item exists at the given position, all of the items will be returned in that collection.

This first example will register two text items. Both of them will be static (that is, they will not auto update). The value of the first one will be Apples and the value of the second one will be Pears. Also, the program registers a callback for mouse clicking. If you click on Apples, a message box will show the word Apples, if you click on Pears, a message box will show the word Pears.

To start off, this code will register the items:

BackgroundColor = "#000000"

RegisterTextItem 0, "ApplesItem", 4, 2, "#ffffff", "InitApplesItem"
RegisterTextItem 0, "PearsItem", 4, 3, "#ffffff", "InitPearsItem"

Sub InitApplesItem(ByVal Item)
   Item.Value = "Apples"
End Sub

Sub InitPearsItem(ByVal Item)
   Item.Value = "Pears"
End Sub

And this is the complete source code. Here I register a callback function to detect clicks, and from there I use the GetItemAt function to see what item the user clicked on.

BackgroundColor = "#000000"

RegisterTextItem 0, "ApplesItem", 4, 2, "#ffffff", "InitApplesItem"
RegisterTextItem 0, "PearsItem", 4, 3, "#ffffff", "InitPearsItem"
RegisterClickCallback "Click"

Sub InitApplesItem(ByVal Item)
   Item.Value = "Apples"
End Sub

Sub InitPearsItem(ByVal Item)
   Item.Value = "Pears"
End Sub

Sub Click(ByVal X, ByVal Y)
	Set Item=GetItemAt(X, Y)
	If Not Item Is Nothing Then
		MsgBox Item.Value
	End If
End Sub

GetItemsAt

This is a constructed example to show how to use the GetItemsAt function. I am sure you can think of more and better examples.

This program holds a bar item and a text item overlaying the bar item. You want to detect clicks on any bar item in the program. This code adds the text and bar items, it also registers a click callback called Click. The click callback uses the GetItemsAt function to determine if the bar was clicked.

BackgroundColor = "#000000"

Bar=RegisterBarItem(1, "B", 3, 2, "#5599aa", "UpdateBar")
GetItem(Bar).Size = 11
GetItem(Bar).ValueVisible = False

RegisterTextItem 0, "T", 4, 2, "#ffffff", "InitText"
RegisterClickCallback "Click"

Sub InitText(ByVal Item)
   Item.Value = "Some text"
End Sub

Sub UpdateBar(ByVal Item)
   Item.Value=Round(Rnd() * 100)
End Sub

Sub Click(ByVal X, ByVal Y)
   Set Items=GetItemsAt(X, Y)
   For Each Item In Items
      If Item.TypeName = "BarItem" Then
         MsgBox "You have clicked on a bar named " & Item.Name & "."
      End If
   Next
End Sub

Note how the click callback function filters out any bar items from the collection, and displays the name of them (just one in this case).

Tweaking the wall, part 3/3: Startup script

The basic rule is that a VBScript file called TheWall.vbs that is placed in the same folder as TheWall.exe is loaded when TheWall.exe is started. Remember that you start TheWall.exe, not any script file – the script file is loaded by TheWall.exe. So if you are creating a shortcut, the shortcut should point to TheWall.exe.

You can manipulate this behavior by passing a filename as an argument to TheWall.exe. If a filename is passed, The Wall attempts to load that file instead of TheWall.vbs. There is no fallback, so the file you pass as an argument, must exist. The Wall cannot be started if no filename is passed as argument and the file TheWall.vbs is not present, or if a filename is passed as an argument and that file does not exist.

To check what file is running, use the ScriptFilename property.

MsgBox ScriptFilename

Just for the fun of it, this code will produce a line that is bouncing within a box. And now you know how to start it.

BackgroundColor = "#000000"

X1 = 0
Y1 = 0
X1Speed = 4
Y1Speed = 2

X2 = 30
Y2 = 190
X2Speed = 2
Y2Speed = 1

BeforeUpdate "Bounce"

Sub Bounce(ByVal G)
   X1 = X1 + X1Speed
   Y1 = Y1 + Y1Speed
   If X1 > 200 Or X1 < 0 Then
      X1Speed = X1Speed * -1
   End If
   If Y1 > 200 Or Y1 < 0 Then
      Y1Speed = Y1Speed * -1
   End If

   X2 = X2 + X2Speed
   Y2 = Y2 + Y2Speed
   If X2 > 200 Or X2 < 0 Then
      X2Speed = X2Speed * -1
   End If
   If Y2 > 200 Or Y2 < 0 Then
      Y2Speed = Y2Speed * -1
   End If

   G.DrawBar "#555555", 0, 0, 200, 200
   G.DrawLine "#ffffff", X1, Y1, X2, Y2
End Sub

Tweaking the wall, part 2/3: Using the graphics drawing interface

The BeforUpdate and AfterUpdate callback functions are basically used to allow custom graphics to be drawn below or above it. The graphics drawing interface is passed as an argument to the function you register as a callback function. Again, this Is the Hello World application of The Wall:

BackgroundColor = "#445566"
RegisterTextItem 0, "A", 4, 4, "#aabbcc", "InitializeText"

Sub InitializeText(ByVal Item)
   Item.Value = "Hello World!"
End Sub

This is what we get:

Hello World!

No, let’s say that I want to draw something before and after the text item that I have registered in the above code, I would have to register these two callback methods too, like so:

BackgroundColor = "#000000"
RegisterTextItem 0, "A", 1, 1, "#00ff00", "InitializeText"

BeforeUpdate "MyBeforeFunction"
AfterUpdate "MyAfterFunction"

Sub MyBeforeFunction(ByVal G)
   'TODO: Use the graphics drawing interface here!
End Sub

Sub MyAfterFunction(ByVal G)
   'TODO: Use the graphics drawing interface here!
End Sub

Sub InitializeText(ByVal Item)
   Item.Value = "Hello World!"
End Sub

Any drawing done in MyBeforeFunction will appear behind the text Hello World! since MyBeforeFunction was registered using the built in BeforeUpdate function. Any drawing done in MyAfterFunction will appear in front of the text since it was registered using the built in AfterUpdate function.

This will give us a blue rectangle behind the text, and a yellow rectangle in front of the text.

BackgroundColor = "#000000"
RegisterTextItem 0, "A", 1, 1, "#00ff00", "InitializeText"

BeforeUpdate "MyBeforeFunction"
AfterUpdate "MyAfterFunction"

Sub MyBeforeFunction(ByVal G)
   G.DrawBar "#0000ff", 0, 0, 400, 60
End Sub

Sub MyAfterFunction(ByVal G)
   G.DrawBar "#ffff00", 0, 65, 400, 60
End Sub

Sub InitializeText(ByVal Item)
   Item.Value = "Hello World!"
End Sub

Like so:

The DrawLine function accepts a color (all colors are in string format), X1, Y1, X2 and Y2. The DrawBar function accepts a color, X, Y, Width and Height, just as the DrawRectangle function.

My bits on evolution, part 1

Some of my bits for the Swedish Skeptic Podcast, Skeptikerpodden, on evolution and creationism, in Swedish.

Några av mina bidrag till svenska Skeptikerpodden om evolution och kreationism, på svenska.

1. “Evolutionism” (från Skeptikerpodden 31/3 2010)

2. Kreationistiska falanger (från Skeptikerpodden 12/4 2010)

3. Bevis för evolution (från Skeptikerpodden 3/5 2010)