Emulation of "Raiden" Game with Mathematica (Blog).
There are a lot of arcade games realized with MMA, including: 2048 (2D and 3D), Sokoban, point 24, Gomoku, Snake, Minesweeper and Tetris. As well as video games like minecraft and SimCity 3000.
However, one classic scrolling shooter arcade game named "Raiden" (means "thunder and lighting") is missing. Here is a prototype of that:
ClearAll["Global`*"]
ClearSystemCache[]
RemoveScheduledTask[ScheduledTasks[]];
DynamicModule[
{
army = {},
bullet = {},
missle = {},
direct = {},
reach = 0,
score = 0,
toClean,
num = 5,
pos,
(*reach=0,*)
reachNum = 10,
RestartAll,
pausing = True,
stop = True,
rand,
least,
launcher,
target,
hit,
explosion = 10,
crash = 10,
bulletSpeed = .6,
enermySpeed = .1,
jet = Graphics[
{Darker@Blue, Polygon[{{0, 0}, {1, 0}, {1, .1}, {0, .1}}],
Polygon[{{0.5, 2}, {.4, .6}, {.6, .6}}],
Polygon[{{-1, 1}, {2, 1}, {.5, 1.2}}],
Thickness -> .02, Arrow[{{.2, .1}, {.2, 1.5}}],
Arrow[{{.8, .1}, {.8, 1.5}}]}, ImageSize -> Tiny],
enermy = Graphics[
{Gray, Polygon[{{.1, 2}, {.9, 2}, {.9, 1.9}, {.1, 1.9}}],
Polygon[{{.5, 0}, {.4, 1.9}, {.6, 1.9}}],
Polygon[{{-1, 1.1}, {2, 1.1}, {.5, .9}}],
Thickness -> .02, Arrow[{{.2, 1.4}, {.2, 0.6}}],
Arrow[{{.8, 1.4}, {.8, .6}}]}, ImageSize -> Tiny]
},
least[bullets_, army_] :=(*Get the local (pseduo) minimum distance between bullets and enermy army*)
Module[{len1 = Length@bullets, len2 = Length@army, frontList, postList, dist, loc, short, long, locShort, locLong},
If[len1 >= len2, frontList = bullets; postList = army, frontList = army; postList = bullets];
dist = (EuclideanDistance @@@ Partition[Riffle[frontList, postList], 2, 1]);
loc = First@Flatten@Position[dist, Min[dist], 1(*,1*)];
locShort = Mod[Mod[loc, 2] + Quotient[loc, 2], Length@postList, 1];
locLong = Quotient[loc, 2] + 1;
short = postList[[locShort]];
long = frontList[[locLong]];
Return[If[len1 >= len2, {locShort, locLong, Min@dist}, {locLong, locShort, Min@dist}]]
];
rand[] := {RandomReal[{-30, 40}], 160};(*Randomly generate enermy flights*)
RestartAll[] := (RemoveScheduledTask[ScheduledTasks[]];
RunScheduledTask[
If[! pausing,
If[(*Randomly generate enermy flights*)
MemberQ[
RandomChoice[
{(Length@army)/num, 1 - (Length@army)/num} -> {False, True},
1
],
True],
AppendTo[army, {enermy, rand[]}]
];
If[(*Handle the situate of collision between the jet and enermy army*)
MemberQ[
Norm[
#[[2]] - MousePosition["Graphics", {0, 0}]
] < crash & /@ army,
True],
(*Speak["Bang"];*)
RemoveScheduledTask[ScheduledTasks[]];
CreateDialog[{TextCell["You Crash!", "Title"], Column@{Row@{"Score: ", score}, Row@{"Passed:", reach}}, DefaultButton[]}]
];
If[(*Handle the situate of enermy army be hit by our bullets*)
Length@army > 0 \[And] Length@bullet > 0,
hit = least[bullet, army[[All, 2]]];
(*Print[hit];*)
If[
Last@hit < explosion,
score += 10;
army = Delete[army, First@hit];
bullet = Delete[bullet, hit[[2]] ]
]
];
If[(*enermy gives out missles*)
Length@army > 0 && RandomReal[] > .05,
launcher = RandomChoice@army[[All, 2]];
(AppendTo[missle, launcher];
AppendTo[direct, Normalize[-launcher + MousePosition["Graphics", {0, 0}]]]);
];
If[(*Handle the situate of being hit*)
MemberQ[
Norm[
# - MousePosition["Graphics", {0, 0}]
] < crash & /@ missle,
True],
RemoveScheduledTask[ScheduledTasks[]];
CreateDialog[{TextCell["You are hit!", "Title"], Column@{Row@{"Score: ", score}, Row@{"Passed:", reach}}, DefaultButton[]}]
];
If[(*If more than reachNum of enermies reach the bottom, lose the game*)
reach >= reachNum,
RemoveScheduledTask[ScheduledTasks[]];
CreateDialog[{TextCell["You Lost the Base!", "Title"], Column@{Row@{"Score: ", score}, Row@{"Passed:", reach}}, DefaultButton[]}]
];
];
1
]);
Panel[ Column@{
Column@{Row@{Text[Style["score: ", Black, 18]], Text[Style[Dynamic[score], Orange, 16]]},
Row@{Text[Style["passed:", Black, 18]], Text[Style[Dynamic[reach], Orange, 16]],
Text[Style["/", Orange, 16]], Text[Style[reachNum, Orange, 16]]},
Button[
Dynamic[If[! stop, "Restart", "Start"]],
RestartAll[];
stop = pausing = False;
score = 0; reach = 0],
Button[
Dynamic[If[! pausing, "Cease", "Resume"]],
If[pausing, pausing = False, pausing = True],
Enabled -> Dynamic[! stop]],
Dynamic[Text[Style[If[! stop, If[! pausing, "fighting", "pausing"], "Stop"], Black, 16]]]},
MouseAppearance[
EventHandler[
Labeled[Deploy@Framed@Graphics[{
PointSize[Large],
Blue,
Dynamic[
Point[(bullet[[All, 2]] += bulletSpeed(*.5*); bullet = Select[bullet, #[[2]] < 150 &]; bullet)], TrackedSymbols :> {bullet}(*Handle the bullets*)
],
Red,
Dynamic[
Point[
(
pos = Position[missle, #] & /@ Select[missle, #[[1]] < -50 || #[[1]] > 50 || #[[2]] > 150 || #[[2]] < 10 &, 1];
If[Length@pos > 0,
toClean = # & @@ pos;
(*Print[toClean];*)
missle = Delete[missle, toClean];
direct = Delete[direct, toClean]];
missle += .5 direct)
],
TrackedSymbols :> {missle}(*Handle the missles*)
],
Dynamic[Thread[Inset[(*Handle the enermy army*)
Magnify[enermy, .5], #
] &[
If[! pausing, reach += Length@Select[army, #[[2, 2]] < 5 &]];
army = Select[army, #[[2, 2]] > 5 &];
army[[All, 2, 2]] -=(*.4*)enermySpeed;
army[[All, 2]]
]],
TrackedSymbols :> {army}]
},
PlotRange -> {{-50, 50}, {0, 150}},
ImageSize -> 300
], Text[Style["Thunder & Lighting", Darker@Green, Italic, 24]], Top],
{"MouseClicked" :> {(*Speak["Shu"];*)AppendTo[bullet, MousePosition["Graphics", {0, 0}]]}}(*Shoot on Mouse Click Action*)
],
Magnify[jet, .5]
]
}]
]