Event Based QTE Mini Games

In MZ I have learned that quick time mini games are possible, but I have no idea how deep you can go with that. I want to make something with out any plugins if possible. If anyone has answers to these questions it would be nice.

1. Can you make a circular QTE bar with a pointer that follows the circle and rotates properly?

2. Can you have multiple QTE with unique button presses at the same time in a single event?

3. Can you have multiple unique QTE based on a choice selection in an event?

Welcome to the forums NiamhNyx!

  1. Show Picture with the Origin set to Center, then Rotate Picture. The rotation axis is whatever origin that picture last used, so a pointer drawn on a transparent canvas the size of your circle, pointer at the top, will spin cleanly around the middle. Rotation speed is -90 to 90 and the engine adds half that many degrees per frame, so speed 2 is one degree a frame, about 6 seconds for a full turn.

The part eventing can’t do is read that angle back out. You’d keep your own variable counting up in the same loop and treat that as the angle, then check it the moment the button gets hit. Rough shape of it, with the ring as picture 1 and the pointer as picture 2, both Origin Center on the same coordinates:

◆Control Variables: #0011 Angle = 0
◆Show Picture: #1, Ring, Center (408,312), (100%,100%), 255, Normal
◆Show Picture: #2, Pointer, Center (408,312), (100%,100%), 255, Normal
◆Rotate Picture: #2, +2
◆Loop
  ◆Control Variables: #0011 Angle += 1
  ◆If: Angle ≥ 360
    ◆Control Variables: #0011 Angle -= 360
  ◆End
  ◆If: Button [OK] is Triggered
    ◆Rotate Picture: #2, 0
    ◆If: Angle ≥ 80
      ◆If: Angle ≤ 100
        ◆Control Switches: #0021 Hit = ON
      ◆End
    ◆End
    ◆Break Loop
  ◆End
  ◆Wait: 1 frame
◆Repeat Above

Speed 2 is one degree per frame, which is why the counter climbs by 1 each pass. The 80 to 100 pair is your hit window, and nesting the two branches is how you get the AND.

If the drift between the counter and the real picture angle worries you, the other route is to skip Rotate Picture entirely and pre rotate the pointer into a set of frames (say 36 at 10 degrees), then Show Picture whichever one matches your counter. More image files, but the angle is always exactly what the variable says.

  1. Event commands run in order, so simultaneous really means one Loop that checks every button each frame. Inside that same loop you can stack a Conditional Branch → Button for each one, each with its own variables and its own pictures, then the single Wait 1 frame at the bottom. The pool is the limit more than the eventing, since that dropdown only offers the engine’s mapped buttons (OK, Cancel, Shift, the four arrows, Page Up, Page Down) and OK and Cancel each cover a few keys. If the arrows are part of the QTE, I think I’d run the whole thing as Autorun rather than Parallel so the player isn’t walking off while pressing them.
  2. That one is fine. Show Choices into separate branches or separate Common Events, one per QTE, each running its own loop and writing a result variable the calling event reads afterward.

Ok, neat so what I have in mind seems possible, but I might have to spend a while trying to figure out how to fully explain it for further clarity. I am just really bad at explaining what it is in my head.

Thank you.

You’re very welcome! No rush on the explaining either, and when you do get to it a rough sketch of what the player sees on screen is easier for me to read than a description anyway. Post it whenever you have it and we can work through the setup.

Ok, so after a few hours of trying to figure this out, I cant get it to work the way I want it to, it will never correctly identify a goal spot or bounce on what I tried to define as a wall.

The mini game I am trying to make here is for brewing potions.

The easy level of the first phase will be putting ingredients in to the cauldron, this phase will have a circle with a goal that moves to a random spot after a successful hit and the pointer bar will bounce at the top of the circle and start to move in the opposite direction.

So far I can get the pointer to rotate around just because of the rotate picture setting in the event, but I can not get it to register the top as a wall and reverse.

◆Loop
  ◆Wait:1 frame
  ◆If:Movement Right Pointer is ON
    ◆Control Variables:#0011 Pointer Angle += 2
    ◆If:Pointer Angle ≥ 360
      ◆Control Switches:#0001 Movement Right Pointer = OFF
      ◆
    :End
    ◆
  :Else
    ◆Control Variables:#0011 Pointer Angle -= 2
    ◆If:Pointer Angle ≤ 1
      ◆Control Switches:#0001 Movement Right Pointer = ON
      ◆
    :End
    ◆
  :End
  ◆
:Repeat Above

This is the first wall I am hitting.

I want to get this part down because It will make it so i can make a double phase for harder potions, where one half of the circle is one input and the other is a different input, with 2 independent goals and pointers that bounce off the middle at the top and bottom.

As well as the goal randomly moving, but that I feel like i can probably solve if i wasn’t having issues with this part.

I have it successfully bouncing off the top now, but I do not know if it is possible to do so with the rotate picture option. So I am using a script to do this.

Is there a better way to get this set up because I plan on making each phase a common event so that it can randomly have a difficulty level pulled during the map event.

◆Loop
  ◆If:Movement Right Pointer is OFF
    ◆Control Variables:#0021 Pointer Angle = 0
    ◆Control Variables:#0022 Pointer Speed = 2
    ◆Show Picture:#1, 01_Circle_Test, Center (552,312), (100%,100%), 255, Normal
    ◆Show Picture:#3, 01_Pointer_Test, Center (552,312), (100%,100%), 255, Normal
    ◆Control Switches:#0001 Movement Right Pointer = ON
    ◆
  :End
  ◆If:Pointer Angle ≥ 360
    ◆Control Variables:#0022 Pointer Speed = -2
    ◆
  :End
  ◆If:Pointer Angle ≤ 0
    ◆Control Variables:#0022 Pointer Speed = 2
    ◆
  :End
  ◆Control Variables:#0021 Pointer Angle += Pointer Speed
  ◆Script:$gameScreen.picture(3)._angle = $gameVariables.value(21);
  ◆Wait:1 frame
  ◆
:Repeat Above

I think the script line is worth keeping. Rotate Picture would handle the bounce too since a negative speed reverses it, but then the engine owns the angle and your variable is only tracking alongside it. Writing _angle keeps both the same number, which is what makes the goal check trustworthy.

What I’d change is where the setup lives. The init block is inside the Loop behind Movement Right Pointer, and I don’t see anything turning that switch back off, so a second call would skip past the Show Pictures and start with whatever angle was left over. Moving it above the Loop does the same job without the switch:

◆Control Variables: #0021 Pointer Angle = 0
◆Control Variables: #0022 Pointer Speed = 2
◆Show Picture: #1, 01_Circle_Test, Center (552,312), (100%,100%), 255, Normal
◆Show Picture: #3, 01_Pointer_Test, Center (552,312), (100%,100%), 255, Normal
◆Loop
  ◆If: Pointer Angle ≥ 360
  ...

From there difficulty is just variables the map event fills in before calling. Pointer Speed for how fast it travels, plus two for the goal window edges, and the common event reads those instead of having numbers typed into it. Then one common event covers every difficulty and the map event picks.

The two pointer phase fits the same loop. Each pointer needs its own angle variable, speed variable and picture number, your limits become 0 to 180 and 180 to 360, and the Script box takes both assignment lines at once.

Huh, I wonder why that suddenly works for me now. It did not seem to work when i was trying it with out the switch, maybe it was something that I removed at some point and didn’t notice.

I now have another two things working so far, Pointer speeding up per hit, and goal randomly changing position for phases that will require that.

I do have a slight issue with the goal currently though. It seems that if the goals center is with in 14 degrees of the top, it does not detect hits consitently.

I assume this is similar to when i was getting false goal hits because i made the hit zone for the moving goal 30 degrees, and on one side it becomes negative and would count everything past that as the goal. But I do not know how to fix it outside of limiting the goals random placement by offsetting it 15 degrees from the top.

This is what i have so far. Just working on getting things working well one at a time.

◆Comment:NOTE TO SELF!!! DONT FORGET TO COMMENT WHAT STUFF DOES!!! 
:       :IF YOU DO YOU WILL REGRET IT MISTER "I WILL REMEMBER LATER"!!!
◆Comment:0021 for pointer angle, 0022 for set pointer speed
:       :0023 for goal center, 0025 for speed increase 
◆Control Variables:#0021 Pointer Angle = 0
◆Control Variables:#0022 Pointer Speed = 2
◆Control Variables:#0025 Increase Speed = 2
◆Comment:Limiting center of goal to 15 degrees from the top for now until I
:       :learn the math needed so it stops ignoring the hits on left side. 
◆Control Variables:#0023 Goal Center Angle = Random 15..345
◆Comment:Load Images for mini game
:       :Remember it layers in order, 1 is bottom. 
◆Show Picture:#1, 01_Circle_Test, Center (552,312), (100%,100%), 255, Normal
◆Show Picture:#2, 01_Goal_Test, Center (552,312), (100%,100%), 255, Normal
◆Show Picture:#3, 01_Pointer_Test, Center (552,312), (100%,100%), 255, Normal
◆Comment:Mini Game Loop
:       :Currently Infinte because no break yet. Dont forget
◆Loop
  ◆Comment:First Two branches for pointer rebounding. 
  ◆If:Pointer Angle ≥ 360
    ◆Control Variables:#0022 Pointer Speed = Increase Speed
    ◆Control Variables:#0022 Pointer Speed *= -1
    ◆
  :End
  ◆If:Pointer Angle ≤ 0
    ◆Control Variables:#0022 Pointer Speed = Increase Speed
    ◆
  :End
  ◆Comment:Angles and rotation
  ◆Control Variables:#0021 Pointer Angle += Pointer Speed
  ◆Script:$gameScreen.picture(3)._angle = $gameVariables.value(21);
  ◆Script:$gameScreen.picture(2)._angle = $gameVariables.value(23);
  ◆Comment:Goal Hit Check Testing
  :       :
  :       :REMEMBER FOR LATER PRESSED DOWN WORKS FOR HOLDS
  :       :USE TRIGGERED FOR SINGLE PRESSES
  ◆If:Button [OK] is being triggered
    ◆Control Variables:#0024 Angle Distance = Pointer Angle
    ◆Control Variables:#0024 Angle Distance -= Goal Center Angle
    ◆If:Angle Distance < 0
      ◆Comment:Stops false goal hits. IMPORTANT TO REMEMBER THIS
      ◆Control Variables:#0024 Angle Distance *= -1
      ◆
    :End
    ◆Comment:Test goal width is 30 degrees. 
    ◆If:Angle Distance ≤ 15
      ◆Comment:Speed increase that works on successful hits
      ◆Control Variables:#0025 Increase Speed += 1
      ◆Comment:Remember the negative speed values exist and need this.
      ◆If:Pointer Speed > 0
        ◆Control Variables:#0022 Pointer Speed = Increase Speed
        ◆
      :Else
        ◆Control Variables:#0022 Pointer Speed = Increase Speed
        ◆Control Variables:#0022 Pointer Speed *= -1
        ◆
      :End
      ◆Comment:Goal placement shifts randomly on the circle. 
      :       :
      :       :Does not work on left side if goal passes over the top.
      :       :Maybe some script can fix, look in to later. 
      ◆Control Variables:#0023 Goal Center Angle = Random 15..345
      ◆Wait:1 frame
      ◆
    :Else
      ◆Comment:Missed goal hit
      :       :
      :       :IDEA!!! Make a point system to determine quality and misses
      :       :reduce it while successful hits increase it. 
      :       :Make phases timed and a max point cap.  
      ◆Wait:1 frame
      ◆
    :End
    ◆
  :End
  ◆Wait:1 frame
  ◆
:Repeat Above

That’s the same wrap issue from before, showing up at the top of the circle now. The distance gets measured the long way around whenever the pointer and the goal sit on opposite sides of 0. Pointer at 358 with a goal at 5 reads as 353 apart instead of 7, so the press gets thrown out even though they look like they’re touching. That’s why it’s inconsistent rather than dead, since a goal at 5 still catches anything pressed on the 5 to 20 side.

One more branch right after the one you already have catches it:

◆If:Angle Distance < 0
  ◆Control Variables:#0024 Angle Distance *= -1
:End
◆If:Angle Distance > 180
  ◆Control Variables:#0024 Angle Distance *= -1
  ◆Control Variables:#0024 Angle Distance += 360
:End

Anything more than 180 apart is the short gap measured backwards, so flipping the sign and adding 360 gives you the real one. 353 becomes 7 and your ≤ 15 check passes. It covers the overshoot as well, since the pointer can sit at 361 or a bit below 0 for a frame before the rebound branches pick it up.

With that in, both Goal Center Angle rolls can go back to Random 0..359, the one at the top and the one in the hit branch.

I see, thank you. Ill add that fix when im back home. It makes one of my overly stupid and complicated ideas seem possible with that fixed.

Here is progress I have made thanks to this.

Ingredient addition mini game

Getting it to track points the way I wanted was a royal pain to get for a while. I have it so if you will lose 1 point if you pass a green goal, mistime a press or hit a red goal.

For a few hours I could not figure out why i was losing points from passing the edge of the goal currently being approached. Turns out I was just dumb and had it count touching the goal as passing it.

I then decided i was going to make it have 2 real goals and 2 fake goals to set the foundation for how i want this to work in game. 2 ingredients are for the potion, the other 2 are random that aren’t.

I don’t even want to talk about the little toss animation for this. Almost 15 hours of just pain and suffering only to finally figure out it was not working because my original pointer was a 688x688 image with that little blue circle and it could not accurately track the location.

The timer was easy at least, but i mean, its a timer.

Looks great, and the point rules read clean, penalizing a pass on a green goal is a nice way to force commitment instead of camping.

The 688 image thing makes sense in hindsight, the picture’s position is the center of the whole canvas, so the dot’s actual spot is never in the variables. If you ever need it again for a toss or a particle, it’s the angle you already track:

x = 552 + r * Math.sin(angle * Math.PI / 180)
y = 312 - r * Math.cos(angle * Math.PI / 180)

Where r is the dot’s distance from the center in pixels. Same convention as your rotation, 0 at the top and clockwise.

I just ended up redoing basically everything since I realized it would be very limited if just used a large rotated image. Have no idea how I am going to set up each of the green goals being a correct ingredient sprite and the red being incorrect, but from what I have been reading it seems like something to do with data base id and a variable set up for it or something. But I’m to tired to look in to it more.

I re-did what I am now calling the easy difficulty of ingredient addtion.
I made the goals 90x90 so i can make the ingredient spites somewhat decently size and have a fair hit box to boot. I also have it set so only 1/8th of the pointer needs to be on the goal to be a successful hit, as i feel it would be unfair other wise.

Difficulty 1

Mid level difficulty took a while to get done, because for SOME UNGODLY REASON I DID NOT COPY PASTE THE PREVIOUS EVENT! THEN LIKE AN ABSOLUTE MORON I SPENT A FULL DAY AND A HALF DEBUGGING STUFF I ALREADY FIXED ON THE EASIER DIFFICULTY…

Difficulty 2

I am still figuring out how to balance the final difficulty though as its going to be absolute hell if you have a momentary lapse in concentration, once its no longer red and green easy to instantly tell apart circles, ohh boy. Also getting rid of the rest of its jank, the goal conga lines still happen occasionally, i feel like if i fine tune the hit detection for the bounce and give the rebound just a tad bit more power it should fix it.

Difficutly 3

Anyways, thanks for letting me know this stuff is possible in rpgmz. Id likely have just been looking for plugins by now if id have not known.

Well, there was a bug i didnt notice because i wasnt paying attention and that was the speed was not going up in steps like i wanted it to and i couldnt sleep knowing it was doing that, so i had to fix it.

For the ingredient sprites I think I’d name each image after a number, like Ingredient_1, Ingredient_2 and so on. If your ingredients are already Items in the database, use their item IDs so Ingredient_12 goes with item 12. The Show Picture window can’t take a variable for the image name, but a Script line can build the name from one:

◆Script:$gameScreen.showPicture(4, "Ingredient_" + $gameVariables.value(31), 1, 552, 312, 100, 100, 255, 0);

That shows whatever ingredient ID is in variable 31 as picture 4. Everything after the name goes in the same order as the Show Picture window: Origin (1 is Center), X, Y, scale X, scale Y, opacity and blend mode. If your loop is already placing the goals every frame, the X and Y only matter until it takes over. Running the line again on the same picture number swaps the image, so it also works when a goal moves to a new spot and needs a new ingredient.

Like the difficulty variables, the map event can fill in the two real ingredient IDs before calling the common event. For the fakes, roll Random over your ingredient range and roll again if it lands on a real one or the other fake. If your real vs fake check already goes by which picture was hit, that part shouldn’t need to change, only the images.

When you deploy, Exclude Unused Files can’t see image names built inside a Script line, so it would strip the ingredient images out. A common event that never gets called, with a plain Show Picture for each ingredient image, keeps them in the build.

This even answers a question i did not know i would eventually have, deployment and it not nuking assets called in scripts.

Thanks again.