-
Shake Window
Version: 1.5
Author: Exhydra
Date: 6-14-2011
Version History
- <Version 1.5> 06.20.2011 - Cleaned up code; Changed WinAPIs slightly.
- <Version 1.4> 06.14.2011 - Cleaned code a little.
- <Version 1.2> 06.14.2011 - [FIX] Determines if the game is full screen and runs a normal screen shake instead.
- <Version 1.1> 06.14.2011 - [FIX] Maintained the game window position through rapid calls.
- <Version 1.0> 06.13.2011 - Original Release
Description
Shaking the screen not enough to emphasize something that happened in game? Want the metor you just dropped on the players to rock more than the pixels inside of the game? Shake Window will allow you to kindly jiggle or ruthlessly rattle the game window in custom directions as well as allow you to shake the screen while you're at it.
Features
- Shake the game window with variable speed, power and direction.
- Shake the screen along with the game window.
- Bypasses the speed and power limitation of the Shake Screen command within the Event Editor.
Screenshots
None
Instructions
See Script Header
Script
#===============================================================================
# Shake Window
#===============================================================================
# Author : Exhydra
# Version : 1.5
# Last Updated : 06/20/2011
#===============================================================================
# Updates
# -----------------------------------------------------------------------------
# » 06/20/11 Cleaned up code; Changed WinAPIs slightly.
# » 06/14/11 [FIX] Maintained the game window position through rapid calls
# [FIX] Determines if the game is full screen and runs a normal
# screen shake instead.
# » 06/13/11 Initial Release.
#===============================================================================
# Future Options
# -----------------------------------------------------------------------------
# » Nothing! Suggestions?
#===============================================================================
# Instructions
# -----------------------------------------------------------------------------
# To Use :
# Call : (from a Script Event command)
# gms = $game_map.screen
# gms.shake_window(power, speed, duration[, route, ds_option])
#
# 'route' Values
# 7 8 9
# 4 (6) - Default; Right then Left (Valid for Window Shake ONLY)
# 1 2 3
#
# 'ds_option' Values
# (0) - Default; Shake Window Only
# 1 - Shake Window and Screen
# 2 - Shake Screen Only
#
# Examples :
# gms = $game_map.screen
# gms.shake_window(5, 5, 15)
#
# gms = $game_map.screen
# gms.shake_window(8, 8, 60, 9, 1)
#===============================================================================
# Description
# -----------------------------------------------------------------------------
# - Shakes the window of the game, or shakes the window and the screen or just
# shakes the screen. This script allows the user to go beyond the limitations
# of the Shake Screen command within the Event Editor. However, that is just
# a by-product and can be easily done without including this script.
#===============================================================================
#===============================================================================
# [NOTE:] -
#===============================================================================
#===============================================================================
# -? Game_Screen
# -----------------------------------------------------------------------------
# Summary of Changes:
# New Method(s) - shake_window
# Aliased Method(s) - clear, update_shake
#===============================================================================
class Game_Screen
#--------------------------------------------------------------------------
# » Clear [ Alias ]
#--------------------------------------------------------------------------
alias ex_shakewindow_clear clear unless $@
def clear
@sw_power = 0
@sw_speed = 0
@sw_duration = 0
@sw_direction = 0
@sw_shake = 0
@sw_hwnd = 0
@sw_szEval = ""
@sw_rect = []
ex_shakewindow_clear
end
#--------------------------------------------------------------------------
# » Shake Window [ New ]
# power : Shake Intensity
# speed : Shake Speed
# duration : Shake Duration (60 = 1 sec)
# [route] : Direction Window will Shake Towards
# 7 8 9
# 4 ?(6) - Default; Right then Left (Valid for Window Shake ONLY)
# 1 2 3
# [ds_option] : 'Double Shake' Option
# (0) - Default; Window Shake Only
# 1 - Window and Screen Shake
# 2 - Screen Shake Only
#--------------------------------------------------------------------------
def shake_window(power, speed, duration, route = 6, ds_option = 0)
return if @sw_shake != 0
ds_option = 2 if Ex_WinAPI.CheckFullScreen == true
case ds_option
when 1
start_shake(power, speed, duration)
when 2
start_shake(power, speed, duration)
return
end
case route
when 1
@sw_direction = 1
@szEval = "@sw_rect[0] + (-@sw_shake), @sw_rect[1] + (@sw_shake)"
when 2
@sw_direction = 1
@szEval = "@sw_rect[0], @sw_rect[1] + (@sw_shake)"
when 3
@sw_direction = 1
@szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1] + (@sw_shake)"
when 4
@sw_direction = -1
@szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
when 6
@sw_direction = 1
@szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
when 7
@sw_direction = -1
@szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1] + (@sw_shake)"
when 8
@sw_direction = -1
@szEval = "@sw_rect[0], @sw_rect[1] + (@sw_shake)"
when 9
@sw_direction = -1
@szEval = "@sw_rect[0] + (-@sw_shake), @sw_rect[1] + (@sw_shake)"
else
@sw_direction = 1
@szEval = "@sw_rect[0] + (@sw_shake), @sw_rect[1]"
end
@sw_power = power
@sw_speed = speed
@sw_duration = duration
@sw_hwnd = Ex_WinAPI.AcquireGameWindow
@sw_rect = Ex_WinAPI.AcquireGameWindowRect
end
#--------------------------------------------------------------------------
# » Update Shake [ Alias ]
#--------------------------------------------------------------------------
alias ex_shakewindow_update_shake update_shake unless $@
def update_shake
if @sw_duration >= 1 or @sw_shake != 0
sw_delta = (@sw_power * @sw_speed * @sw_direction) / 10.0
if @sw_duration <= 1 and @sw_shake * (@sw_shake + sw_delta) < 0
@sw_shake = 0
else
@sw_shake += sw_delta
end
if @sw_shake > @sw_power * 2
@sw_direction = -1
end
if @sw_shake < - @sw_power * 2
@sw_direction = 1
end
if @sw_duration >= 1
@sw_duration -= 1
end
if Ex_WinAPI.CheckFullScreen != true
eval("Ex_WinAPI.SetWindowPosition(@sw_hwnd, " + @szEval + ",
@sw_rect[2] - @sw_rect[0], @sw_rect[3] - @sw_rect[1])")
end
end
ex_shakewindow_update_shake
end
end
class Ex_WinAPI
#--------------------------------------------------------------------------
# » Windows API Declarations [ Windows API ]
#--------------------------------------------------------------------------
@@getCurrentThreadId = Win32API.new('kernel32' ,'GetCurrentThreadId' , [''] , 'L')
@@getWindowLong = Win32API.new('user32' ,'GetWindowLongA' , ['L', 'I'] , 'L')
@@getWindowRect = Win32API.new('user32' ,'GetWindowRect' , ['L', 'P'] , 'L')
@@getWindowThreadProcessId = Win32API.new('user32' ,'GetWindowThreadProcessId', ['L', 'P'] , 'L')
@@getClientRect = Win32API.new('user32' ,'GetClientRect' , ['L', 'P'] , 'L')
@@setWindowPosition = Win32API.new('user32' ,'SetWindowPos' , ['L', 'L', 'I', 'I', 'I', 'I', 'I'] , 'I')
@@findWindowEx = Win32API.new('user32' ,'FindWindowEx' , ['L', 'L', 'P', 'P'] , 'L')
#--------------------------------------------------------------------------
# » Constants
#--------------------------------------------------------------------------
@@gwl_STYLE = -16
@@ws_MINIMIZEBOX = 0x20000
@@ws_DLGFRAME = 0x00400000
@@ws_BORDER = 0x00800000
@@ws_SYSMENU = 0x00080000
@@ws_CLIPSIBLINGS = 0x04000000
@@ws_VISIBLE = 0x10000000
@@ws_RGSS_NORM = @@ws_SYSMENU | @@ws_CLIPSIBLINGS | @@ws_VISIBLE |
@@ws_BORDER | @@ws_DLGFRAME | @@ws_MINIMIZEBOX
#--------------------------------------------------------------------------
# » Variables
#--------------------------------------------------------------------------
@@gameHwnd = nil
#--------------------------------------------------------------------------
# » Acquire Game Window [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireGameWindow
return @@gameHwnd if @@gameHwnd
procID = [0].pack('L')
threadID = @@getCurrentThreadId.call
gameTitle = ($data_system.nil?) ? 0 : $data_system.game_title
@@gameHwnd = 0
begin
@@gameHwnd = @@findWindowEx.call(0, @@gameHwnd, "RGSS Player", gameTitle)
if @@gameHwnd
wndThreadID = @@getWindowThreadProcessId.call(@@gameHwnd, procID)
return @@gameHwnd unless wndThreadID != threadID; gameTitle = 0
end
end until @@gameHwnd == 0
raise "ERROR : RGSS player window not found!"
return 0
end
#--------------------------------------------------------------------------
# » Acquire Game Window Long [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireGameWindowLong
wHwnd = self.AcquireGameWindow
return @@getWindowLong.call(wHwnd, @@gwl_STYLE)
end
#--------------------------------------------------------------------------
# » Acquire Game Window Rect [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireGameWindowRect
aRect = [0,0,0,0].pack('L4')
wHwnd = self.AcquireGameWindow
@@getWindowRect.call(wHwnd, aRect)
l, t, r, b = aRect.unpack('L4')
return l, t, r, b
end
#--------------------------------------------------------------------------
# » Acquire Client Screen Rect [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireClientScreenRect
aScreen = [0,0,0,0].pack('L4')
wHwnd = self.AcquireGameWindow
@@getClientRect.call(wHwnd, aScreen)
l, t, r ,b = aScreen.unpack('L4')
return l, t, r, b
end
#--------------------------------------------------------------------------
# » Check Full Screen [ Windows API ]
#--------------------------------------------------------------------------
def self.CheckFullScreen
aRect = self.AcquireGameWindowRect
aScreen = self.AcquireClientScreenRect
lCurrScreen = self.AcquireGameWindowLong
if aRect[0] <= aScreen[0] and
aRect[1] <= aScreen[1] and
aRect[2] >= aScreen[2] and
aRect[3] >= aScreen[3] or
lCurrScreen | @@ws_RGSS_NORM != @@ws_RGSS_NORM
return true
else
return false
end
end
#--------------------------------------------------------------------------
# » Set Window Position [ Windows API ]
# window_hWnd : Window HWND
# window_x : Window Screen X (Top Left)
# window_y : Window Screen Y (Top Left)
# window_w : Window Width
# window_h : Window Height
#--------------------------------------------------------------------------
def self.SetWindowPosition(window_hWnd, window_x, window_y, window_w, window_h)
return @@setWindowPosition.call(window_hWnd, 0, window_x, window_y, window_w, window_h, 0)
end
end
Credit
Thanks
- <Numerous Script Authors; Windows API> (GetPrivateProfileString, Find Window, AquireGameWindow)
Support
- Please post in this thread for support.
Known Compatibility Issues
Demo
None
Author's Notes
- First consolidated script! It still feels a little bulky and I'm looking at different ways to streamline the way I did things. Any help with optimization is appreciated.
Restrictions
- Free for use in non-commercial and commercial games.
-
Looks great Exhyrda, a nice script to enter the scripting scene proper. ;)
For your issues regarding the window falling off the screen, if you have a conditional in an update method or something similar to check if window.y < 0 or window.y > 544 (off the top of my head, I can't remember the proper dimensions XD) you can just have a method run to keep the window on the screen. I'll give it a shot later tonight.
For your full screen thingamy, you could use a similar way of finding that out: there are API's required to make the game window fullscreen, but I can't remember any of them... yeah. I'll get back to you later on about that.
Looks cool, welcome to the world of scripting (MA's the boss). I look forward to more works.
-
Very cool script, bro.
-
Okay, I think I found that Win32API thing I was talking about. From an old full-screen I wrote yonks ago, I found that to activate fullscreen I used the following call.
$keybd = Win32API.new ('user32.dll', 'keybd_event', ['i', 'i', 'l', 'l'], 'v')
$keybd.call 0xA4, 0, 0, 0
$keybd.call 13, 0, 0, 0
$keybd.call 13, 0, 2, 0
$keybd.call 0xA4, 0, 2, 0
So in a conditional to test if the window is in fullscreen, see if you can't use this.if $keybd == Win32API.new ('user32.dll', 'keybd_event', ['i', 'i', 'l', 'l'], 'v')
I think that should work, I'll give it a shot when I'm on a computer with VX.
EDIT:: Dang, doesn't work. I'll look into it more.
-
Not the most elegant solution, but you can run a check for Ex_WinAPI.AcquireGameWindow to see if it's [0, 0, 640, 480], which is always the value that RPG Maker VX uses for its fullscreen option.
Otherwise, really neat script!
-
Hmm, stopping the screen slide was easier than I thought. I just made sure that @sw_shake was always zero before the script could be started again ('return if @sw_shake != 0') and that seems to work fine. I shoved version 1.1 into a parallel process and it didn't budge after an extended period of time. I also cleaned up some of the @szEval code and made it smaller, although I still think there must be an easier way to do what I did.
However, checking for full screen is still a bit of a problem. I don't think it would be reliable to check for a certain screen size, as people may alter theirs with resolution modifying scripts. What I'm probably going to have to do is check the game window size versus the monitor window size, and if they overlap then the game is full screen. I've been scouring for an easy solution within the RPGVX data structures, looking for a flag that declares that full screen is enabled, but it doesn't look like there is one. You'd think that there would be ... ah well.
Thanks for the feedback so far!
EDIT : Here's a work-around script, but it feels kind of dirty how I'm determining full screen. Basically comparing GetWindowLong information beside each other instead of checking for a specific window style. I updated the code in the original post.
EDIT II : Here is the code that I tried using before, and it seems to me that it would be better for me to narrow down which window styles (WS_STYLE) are being changed rather than just comparing one GetWindowLong number against the other. I commented in where I was running into problems. All of the available window styles are listed here :
Normal Window Styles : http://msdn.microsoft.com/en-us/library/ms632600%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/ms632600%28v=vs.85%29.aspx)
Extended Window Styles : http://msdn.microsoft.com/en-us/library/ff700543(v=vs.85).aspx (http://msdn.microsoft.com/en-us/library/ff700543(v=vs.85).aspx)
class Ex_FSCheck
# Changing GWL_EXSTYLE to -20 enables checking for extended window styles
@GWL_STYLE = -16
@WS_CAPTION = 0x00C00000
@WS_VISIBLE = 0x10000000
@WS_DISABLED = 0x08000000
@WS_MAXIMIZEBOX = 0x10000
@WS_MINIMIZEBOX = 0x20000
@WS_POPUP = 0x80000000
@WS_EX_MDICHILD = 0x00000040
@WS_EX_COMPOSITED = 0x02000000
def self.AcquireGameWindowLong(nIndex = @GWL_STYLE)
get_long = Win32API.new('user32.dll', 'GetWindowLongA', ['L', 'I'], 'l')
window_hwnd = self.AcquireGameWindow
wnd_tmp = get_long.call(window_hwnd, nIndex)
# Is this how I would check these two against each other?
# This -always- returns true. Even if I use @WS_DISABLED, and I know the window certainly isn't disabled.
if wnd_tmp && @WS_POPUP == @WS_POPUP
return true
else
return false
end
end
def self.AcquireGameWindow
window_title = GetPrivateProfileString("Game", "Title", "", "./Game.ini")
return FindWindow("RGSS Player", window_title)
end
def self.FindWindow(window_class, window_title)
find_window = Win32API.new('user32.dll', 'FindWindowA', ['P', 'P'], 'l')
return find_window.call(window_class, window_title)
end
def self.GetPrivateProfileString(ini_section, ini_key, default_val, ini_file)
get_string = Win32API.new('kernel32.dll', 'GetPrivateProfileStringA', ['P', 'P', 'P', 'P', 'L', 'P'], 'l')
sz_buffer = "\0" * (256 + 1)
get_string.call(ini_section, ini_key, default_val, sz_buffer, 256, ini_file)
return sz_buffer.delete!("\0")
end
end
EDIT III : I found another way to check for full screen, but I feel that it's also a bit flimsy. Well, either way I have lined out works fine, but I'm sure that there are better ways of going about doing it ...
EDIT IV : Alright, narrowed it down a little further. Close enough, I think.
class Ex_FSCheck
#===============================================================================
# Full Screen Check
#===============================================================================
# Author : Exhydra
# Version : 1.5
# Last Updated : 07/14/2011
#===============================================================================
#--------------------------------------------------------------------------
# ? Windows API Declarations [ Windows API ]
#--------------------------------------------------------------------------
@getCurrentThreadId = Win32API.new('kernel32', 'GetCurrentThreadId' , [''] , 'L')
@getWindowRect = Win32API.new('user32', 'GetWindowRect' , ['L', 'P'] , 'L')
@getWindowThreadProcessId = Win32API.new('user32', 'GetWindowThreadProcessId', ['L', 'P'] , 'L')
@getClientRect = Win32API.new('user32', 'GetClientRect' , ['L', 'P'] , 'L')
@findWindowEx = Win32API.new('user32', 'FindWindowEx' , ['L', 'L', 'P', 'P'] , 'L')
#--------------------------------------------------------------------------
# ? Acquire Game Window [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireGameWindow
procID = [0].pack('L')
threadID = @getCurrentThreadId.call
nextWindow = 0
begin
nextWindow = @findWindowEx.call(0, nextWindow, "RGSS Player", 0)
if nextWindow
wndThreadID = @getWindowThreadProcessId.call(nextWindow, procID)
if wndThreadID == threadID
return nextWindow
end
end
end until nextWindow == 0
raise "ERROR : RGSS player window not found!"
return 0
end
#--------------------------------------------------------------------------
# ? Acquire Game Window Rect [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireGameWindowRect
aRect = [0,0,0,0].pack('L4')
wHwnd = self.AcquireGameWindow
@getWindowRect.call(wHwnd, aRect)
l, t, r, b = aRect.unpack('L4')
return l, t, r, b
end
#--------------------------------------------------------------------------
# ? Acquire Client Screen Rect [ Windows API ]
#--------------------------------------------------------------------------
def self.AcquireClientScreenRect
aScreen = [0,0,0,0].pack('L4')
wHwnd = self.AcquireGameWindow
@getClientRect.call(wHwnd, aScreen)
l, t, r ,b = aScreen.unpack('L4')
return l, t, r, b
end
#--------------------------------------------------------------------------
# ? Check Full Screen [ Windows API ]
#--------------------------------------------------------------------------
def self.CheckFullScreen
aRect = self.AcquireGameWindowRect
aScreen = self.AcquireClientScreenRect
if aRect[0] <= aScreen[0] and
aRect[1] <= aScreen[1] and
aRect[2] >= aScreen[2] and
aRect[3] >= aScreen[3]
return true
else
return false
end
end
end