Adding Virtual Resolution to Grey Alien Framework
BlitzMax Forums/BlitzMax Programming/Adding Virtual Resolution to Grey Alien Framework
| ||
Hi All, Just wondering apart from Jake has anyone added Virtual Resolution support to Grey Alien Framework (v1.11)? If so could you please share your code? I've started to add support for it based on this post: http://www.blitzbasic.com/Community/posts.php?topic=94408 I've added this to Commoncode.bmx at line 144: Global WindowWidth:Int = 800 Global WindowHeight:Int = 600 Global VirtualRatioX:Float Global VirtualRatioY:Float Global UsingVirtualResolution:Int = 0 Then in CommonTypes.bmx: TGame.GraphicsCreate after the If FirstTime statement (Line 858): Then change all the Graphics commands to use WindowWidth and WindowHeight instead of ScreenWidth and ScreenHeight eg: If GraphicsModeExists(WindowWidth, WindowHeight, 32) Then OK = Graphics(WindowWidth, WindowHeight, 32, Hertz) Depth = 32 EndIf Then right at the bottom of TGame.GraphicsCreate: DoSetVirtualResolution() and add DoSetVirtualResolution() to TGame: Method DoSetVirtualResolution() If ScreenWidth <> WindowWidth Then SetVirtualResolution(ScreenWidth, ScreenHeight) VirtualRatioX = ScreenWidth / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) UsingVirtualResolution = 1 End If End Method So I've done all this and having issue with the mouse stuff... I added this to TMousePointer.UpdateCoords: If pt <> Null Then 'Use client area position so we can see if the cursor is outside of window. Game.SetClientCoords() 'Need this in case window has been dragged as drag code doesn't alway set it soon enough. If UsingVirtualResolution Then x = (pt.x - Game.ClientX) * VirtualRatioX y = (pt.y - Game.ClientY) * VirtualRatioY Else x = pt.x - Game.ClientX y = pt.y - Game.ClientY EndIf EndIf But this messes up the Menu mouse overlaps... Last edited 2011 Last edited 2011 |
| ||
Okay... Not sure why but GAF sets Mouse.X and Mouse.Y in TMousePointer.UpdateCoords and TGame.DoPeekEvent, so I've added the ratio stuff there too: Case EVENT_MOUSEMOVE If UsingVirtualResolution Then Mouse.X = EventX() * VirtualRatioX Mouse.Y = EventY() * VirtualRatioY Else Mouse.X = EventX() Mouse.Y = EventY() End If Is there anything else I need to be aware of with the Virtual Resolution setting in GAF? Thanks! |
| ||
Hmmm strange... I changed my screen resolution to 800x600 and ran this code: For some reason it only displays the line from 0, 0 to 640, 480 not the full screen, there is a slight flicker at the start which looks like it does initially draw the line 0,0,1024,768. I noticed that in my GAF game when I was alt-tabbing it also does this effect, If Suspended = True 'should resume the music/speech? (only if not paused) If UsingVirtualResolution Mouse.ClearButtons() GraphicsCreate() RestoreMouseCoords() End If [edit] Just found that Chroma raised this issue: http://www.blitzbasic.com/Community/posts.php?topic=93081 And his fix is alot cleaner than mine so ignore the above and just add this to DoSetVirtualResolution: Method DoSetVirtualResolution() If ScreenWidth <> WindowWidth Then SetVirtualResolution(ScreenWidth, ScreenHeight) VirtualRatioX = ScreenWidth / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) SetViewport 0, 0, ScreenWidth, ScreenHeight UsingVirtualResolution = 1 End If End Method [/edit] Last edited 2011 Last edited 2011 |
| ||
Final post of the night... I've found a couple of posts saying that the Virtual Resolution code doesnt work for DirectX7 on certain PCs... http://www.blitzbasic.com/Community/posts.php?topic=94446#1083571 So do I disable the Virtual resolution stuff if the player only has DX7 installed? Also I'm a bit worried about this post too: http://www.blitzbasic.com/Community/posts.php?topic=94616#1086210 What was the conclusion? Thanks! Last edited 2011 |
| ||
You also need to modify TScreen.ImageLoad() so that it uses Flags-1 if UsingVirtualResolution is true so that images are smoothed when scaled. It's true that viewport used to have issues, but I've used the viewport in DX9 with Virtual Resolution no problems with no reports of fails (it might still fail, but I bet number of PCs it fails on is low). DoSetVirtualResolution() is called after I successfully set fullscreen in Graphics Create: Local OK:TGraphics = Null If FullScreen Then ... If FullScreen Then DoSetVirtualResolution() EndIf and after setting Windowed mode: If OK = Null Then ... Else DoSetVirtualResolution() EndIf You are indeed correct with adding SetViewport(0, 0, ScreenWidth, ScreenHeight) to DoSetVirtualResolution(). I did that too. Yes I'm only using it for DX9. I'm now using DX9 by default with the framework. One issue is the input lag on DX9 in full-screen and windowed mode. This works in full-screen, but not windowed (in windowed I offer users the option of turning off the custom cursor). ' ----------------------------------------------------------------------------- ' ccFixLagDX9: Fixes DirectX9 rendering lag in BlitzMax ' ----------------------------------------------------------------------------- Function ccFixLagDX9() Local Temp:TPixmap Temp = GrabPixmap(1, 1, 1, 1) End Function Call it before ccFlip() in PostDraw(). ?Win32 'This lag fix works for DX9 and OpenGL. If (DriverDX9 Or Driver = 1) And FullScreen Then ccFixLagDX9() ? To fix mouse issues I did this to DoMoveMouse() Method DoMoveMouse(x:Int, y:Int) 'This correct scales the coords if a virtual resolution is being used. If UsingVirtualResolution = 1 'This is untested if Full Screen is bigger than windowed e.g. 1024x768 fullscreen and only 800x600 in windowed due to Desktop res of 1024x768. MoveMouse x / VirtualRatioX, y / VirtualRatioY Else ... End If End Method Also modified the EVENT_MOUSE_MOVE similar to your code but with ccRound on there Case EVENT_MOUSEMOVE 'If the virutal resolution doesn't match the screen size in full screen or windowed mode, we need to scale the coordinates to compensate. If UsingVirtualResolution Then Mouse.X = ccRound(EventX() * VirtualRatioX) Mouse.Y = ccRound(EventY() * VirtualRatioY) Else Mouse.X = EventX() Mouse.Y = EventY() EndIf Also tweaked TMousePointer.UpdateCoords here: If pt <> Null Then ... x = pt.X-Game.ClientX y = pt.Y-Game.ClientY 'If the virtual resolution doesn't match the screen size, we need to scale the coordinates to compensate. If Game.UsingVirtualResolution Then x = ccRound(x * Game.VirtualRatioX) y = ccRound(y * Game.VirtualRatioY) EndIf EndIf Oh one last thing, change ccFixMacWindowY() to use WindowHeight not ScreenHeight. All that stuff took a lot of figuring out and testing but it worked well in the end and I shipped Spring Bonus with it. Thanks for being careful to only post modifications and not original code, like I've been. BFG own the original as I'm sure you know, so I can't post that code, but mods should be fine. Last edited 2011 Last edited 2011 |
| ||
Thanks Jake :) I'll test this tonight... I notice that the minimum specs for Spring Bonus is still DirectX7, but you have updated your framework to DX9. Does this mean SB doesnt run on a machine with DX7? [edit] Jake replied via email to me: If it detects no DX9 it will use DX7 but won't use SetVirtualRes. So if a PC only has DX7 it will display at your screenwidth/height and will not use the virtual resolution. Thanks a lot Jake :) Last edited 2011 |
| ||
I've just changed DoSetVirtualResolution to this:Method DoSetVirtualResolution() If DriverDX9 Or Driver = 1 If ScreenWidth <> WindowWidth Then SetVirtualResolution(ScreenWidth, ScreenHeight) VirtualRatioX = ScreenWidth / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) SetViewport 0, 0, ScreenWidth, ScreenHeight UsingVirtualResolution = 1 Else UsingVirtualResolution = 0 End If Else UsingVirtualResolution = 0 EndIf End Method So if you give the user the option to switch between graphics drivers it will turn on or off the virtual resolution if needed. |
| ||
Ah I see. Well I'm making it automatic that it'll drop to DX7 but if they have 9 it'll use that, so no choice :-) |
| ||
Its more the choice between OpenGL and DirectX ;) So if they have DirectX7 installed they can't use the virtual resolution, but if you switch to OpenGL it will use it... and the new code allows the user to then switch back to DirectX7 without issues (as the variable is now set back to 0 so it doesnt mess up with the mouse coordinates etc). |
| ||
Yes it's good. It's just that I don't allow sriver swapping in my casual games, but I probably would if I shipping an indie style game. |
| ||
Any idea why switching between window mode and full screen causes a 50% drop in the FPS rate? Using DirectX9 and in window mode I get 60FPS, as soon as I go into Fullscreen mode (without the virtual res) it drops to 30FPS!!! When using OpenGL it is fine, 60FPS in fullscreen and 60FPS in window mode... [edit] Found it... the slow down is caused by ccFixLagDX9... :/ The PC specs which slowed down in Full screen mode and DX9: CPU: Intel Core 2 Duo E8400 3GHz RAM: 2GB GPU: NVIDIA Quadro NVS 290 (driver 275.36) OS: Windows 7 Enterprise (32bit) So the question is: to lag the mouse or to lag the game?! Last edited 2012 Last edited 2012 |
| ||
Wow really, so it's missing a whole frame on that system if it's dropping to 30FPS. Is that GPU any good? Sounds like going back to DX7 is the solution sigh. But then no virtual res. Last edited 2012 |
| ||
I'm investigating how to add hardware mouse cursors to Blitzmax, as thru my research I believe this is the "correct" fix to the mouse lag issue in DirectX games. No promises though ;) No virtual res = no game >800x600... For a short term workaround, maybe a simple IF statement to check the performance is < 50(?) FPS then disable the ccLagFix.... Last edited 2012 |
| ||
I've found some code on the Unity forum which can load a .cur file and I've added it to Blitzmax... the only issue I've got now is that my image is 64x64 but when I use it, it is resized to 32x32 :/ http://forum.unity3d.com/threads/70163-Hardware-Cursor-Plugin Heres the c++ code: extern "C" __declspec( dllexport ) void InitializeCursor(int cursorId, char* standardPath) { wchar_t newPath[1024]; MultiByteToWideChar(CP_UTF8, 0, standardPath, -1, newPath, 1024); HCURSOR cursor = ::LoadCursorFromFileW(newPath); //HCURSOR cursor = LoadCursor(NULL, IDC_WAIT); cursors[cursorId] = cursor; } extern "C" __declspec( dllexport ) void SetCurrentCursor(int cursorId) { HWND result = GetForegroundWindow(); SetClassLongA(result, -12, (long)cursors[cursorId]); SetCursor(cursors[cursorId]); InvalidateRect(result, NULL, TRUE); } And the BlitzMax: ?win32 Extern Function InitializeCursor(cursorId:Int, path$z) Function SetCurrentCursor(cursorId:Int) EndExtern ? Function ccInitializeCursor(id:Int, path:String) ?win32 InitializeCursor(id, path) ? End Function Function ccSetCurrentCursor(id:Int) ?win32 SetCurrentCursor(id) ? End Function And to use: ccInitializeCursor(0, "pointer.cur") ccSetCurrentCursor(0) Currently I've got my pointer.cur in the same folder as the exe. [edit] According to this Microsoft page, the system imposes a standard size of 32x32 and will only use 64x64 if the DPI is too high... http://support.microsoft.com/kb/307213 [/edit] Last edited 2012 |
| ||
Very interesting. Don't forget that the lag is a *display lag* so keys also appear to be slow as well, not just the mouse. Not a problem for casual games, but definitely for arcade games. In fact I first discovered this lag making a platformer minigame that used keys. Last edited 2012 |
| ||
Ah yes - I did forget about the lag keys/joysticks too... Using a hardware mouse cursor does totally get rid of the lag with the mouse though ;) |
| ||
Good to know thanks. |
| ||
Oh and btw, for Spring Bonus what I did was add the lagfix code and had an option to turn off the custom cursor and just display the windows cursor instead as a lot of people on BFG want that option, and that seemed fine. I didn't get any reports of horrid 30FPS issues, but maybe they didn't notice! If you use a custom hardware cursor it just occurred to me that any clicks you make or "drawing"/dragging you did with that cursor would seem to lag behind due to the display lag. For example if you pick up objects that stick to the cursor and are dropped elsewhere, they would appear to lag behind the cursor. |
| ||
for Spring Bonus what I did was add the lagfix code Ah cool... I was going to ask if you added the lagfix to Spring Bonus :) you did with that cursor would seem to lag behind due to the display lag Yep that is true... it seems that there really isnt a good solution apart from turning vsync off :( |
| ||
To bring this back on topic, I've added Widescreen support to GAF - thanks to James' code: http://www.blitzbasic.com/codearcs/codearcs.php?code=2879#comments In CommonTypes.bmx Declare the main variable "WideScreen" Type TGame ... 'Screen and timing vars Field FullScreen:Int = 1 Field WideScreen:Int = 0 ... Add it to the ini file: '-------------------- 'Ini '-------------------- Method IniFill() ... Ini.Add("WideScreen", WideScreen) End Method More ini file stuff Method IniExtract() 'Extract the Game variables from the Ini object ... Case "WIDESCREEN" WideScreen = Int(Line.Value) End Select ... And the guts of the Virtual resolution stuff with added Widescreen support: Method DoSetVirtualResolution() If DriverDX9 Or Driver = 1 ' Reset of display needed when re-calculating virtual graphics stuff/clearing borders... SetVirtualResolution GraphicsWidth (), GraphicsHeight () SetViewport 0, 0, GraphicsWidth (), GraphicsHeight () SetOrigin 0, 0 ' Got to clear both front AND back buffers or it flickers if new display area is smaller... Cls Flip Cls Flip Cls Flip Cls Flip If FullScreen And WideScreen Local gwidth:Int Local gheight:Int gwidth = DesktopWidth gheight = DesktopHeight Local virtualratio:Float = Float(ScreenWidth) / Float(ScreenHeight) Local graphicsratio:Float = Float (gwidth) / Float (gheight) Local gtovratio:Float = graphicsratio / virtualratio Local vtogratio:Float = virtualratio / graphicsratio If graphicsratio >= virtualratio Local vscale:Float = Float (gheight) / Float(ScreenHeight) Local pixels:Float = Float(ScreenWidth) / (1.0 / vscale) Local half_scale:Float = (1.0 / vscale) / 2.0 Local sx:Float = Float(ScreenWidth) * gtovratio SetVirtualResolution sx, ScreenHeight vxoff = (gwidth - pixels) * half_scale vyoff = 0 SetViewport vxoff, vyoff, ScreenWidth, ScreenHeight SetOrigin vxoff, vyoff VirtualRatioX = sx / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) UsingVirtualResolution = 1 Else Local vscale:Float = Float (gwidth) / Float(ScreenWidth) Local pixels:Float = Float(ScreenHeight) / (1.0 / vscale) Local half_scale:Float = (1.0 / vscale) / 2.0 Local sy:Float = Float(ScreenHeight) * vtogratio SetVirtualResolution ScreenWidth, sy vxoff = 0 vyoff = (gheight - pixels) * half_scale SetViewport vxoff, vyoff, ScreenWidth, ScreenHeight SetOrigin vxoff, vyoff VirtualRatioX = ScreenWidth / Float(WindowWidth) VirtualRatioY = sy / Float(WindowHeight) UsingVirtualResolution = 1 EndIf Else If ScreenWidth <> WindowWidth Then SetVirtualResolution(ScreenWidth, ScreenHeight) VirtualRatioX = ScreenWidth / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) SetViewport 0, 0, ScreenWidth, ScreenHeight UsingVirtualResolution = 1 Else If Not WideScreen Then UsingVirtualResolution = 0 End If EndIf Else UsingVirtualResolution = 0 EndIf End Method And a helper toggle method: Method ToggleWideScreen() If Self.WideScreen WideScreen = 0 vxoff = 0 vyoff = 0 UsingVirtualResolution = 0 Else WideScreen = 1 End If Mouse.ClearButtons() 'for safety in case a button was used to togglefullscreen If Game.FullScreen Then DoSetVirtualResolution() RestoreMouseCoords() IniUpdate() FixedRateLogic.Init() 'avoids skips due to old variable values FixedRateLogic.HistoryClear() End Method Done :) Last edited 2012 |
| ||
Wow that is coolness! Thanks for sharing. So how are you using this yourself? Are you making a widescreen game (16:9) and using the system to add bars above and below on 4:3 screens (and small ones on 16:10 screens and any other sizes)? And how are you deciding on the resolution to make the window/fullscreen? Some people decided to make full screen the same as the desktop but other said it slowed their game down a lot. When I looked into making GAF widescreen a while ago there were lots of differing opinions as to how to handle it and I didn't need it myself at the time so didn't add it it. If anything, for casual games, it's just needed for giving the user an option to add bars to the side for 4:3 games shown on a widescreen monitor so the game is the correct aspect ratio, not stretched (as some monitors do and others don't, and some allow you to toggle). |
| ||
couple of code questions: what is this for? If Not WideScreen Then UsingVirtualResolution = 0 and how come you only call DoSetVirtualResolution() for FullScreen mode in the toggle function? Sorry it's late and I haven't tested myself yet so it's not obvious :-) |
| ||
So how are you using this yourself? I'm coding at 1024x768 so 4:3 and I've added an option to the option screen so the user can switch between the correct ratio of the game (by turning on widescreen) or having the game stretched across the screen. If Not WideScreen Then UsingVirtualResolution = 0 Just to ensure that the UsingVirtualResolution boolean is turned off. how come you only call DoSetVirtualResolution() for FullScreen mode in the toggle function? As I've only coded the Virtual resolution stuff to work in full screen and the user can enable widescreen mode if the game is in window mode. |
| ||
Hi therevills! Thank you for the improvements! I will try it surely. One question please: How do you added the DX9 in the framework? I made an experiment a time ago, with only dx9, seems it works well, but i need to change the blitzmax source code, adding a method in the dx module ( it's only one line... ) but I don't like touch the blitzmax code... Regards |
| ||
@therevills: One thing I notice in your code there:Local gwidth:Int ' ... gwidth = DesktopWidth DesktopWidth () is a valid BMX function, so running just the two lines above will fail (DesktopWidth in this case is a function pointer). I therefore assume you've got a global Int called DesktopWidth somewhere that's wiping out the BMX function, as this compiles: Local DesktopWidth:Int = 10 Local gwidth:Int gwidth = DesktopWidth Thought I ought to point it out anyway. |
| ||
@therevills: Ah yes that makes sense thanks, now I read it in the morning :-) This code could be used to write a widescreen HOG or something though right? @BlitzSupport: Yes DesktopWidth is a global that is set elsewhere in teh framework. Last edited 2012 |
| ||
This code could be used to write a widescreen HOG or something though right? Yep! James did a brilliant job with this :) How do you added the DX9 in the framework? Opps.. yeah thats really needed on this post too... In CommonTypes.bmx: Type TGame ... Field DriverDX9:Int = 0 Method GraphicsCreate() ... 'Set DirectX or BlitzMax OpenGL driver (not pure OpenGL) Local DriverOK%=0 ?Win32 If Driver=0 Then 'DirectX DebugInt = 101 'Is there a Direct3D 7 Driver installed? If D3D9Max2DDriver() Then DebugInt=10101 'The code can reach here even if the driver is not DirectX7 or higher I've discovered. If Not ccDirectXVersionIsValid() Then 'Make sure that ccGraphicalErrors=0 otherwise this error won't show! ccRunTimeError("Sorry, you need DirectX V7.0 or higher!") Else DirectXVersion = ccGetDirectXVersion() EndIf DebugInt=102 SetGraphicsDriver D3D9Max2DDriver() DriverOK = 1 DriverDX9 = 1 ElseIf D3D7Max2DDriver() Then ... Else 'OpenGL DebugInt=106 'Is there an OpenGL Driver installed? If GLMax2DDriver() Then DebugInt=107 SetGraphicsDriver GLMax2DDriver() DriverOK = 1 ElseIf D3D9Max2DDriver() Then DebugInt = 10101 SetGraphicsDriver D3D9Max2DDriver() DriverOK = 1 Driver = 0 DebugInt = 110 IniUpdate() DriverDX9 = 1 Else 'Hmm, OK, OpenGL not present, is DirectX available? DebugInt=108 If D3D7Max2DDriver() Then ... ?Win32 'Only call this in DirectX mode 'This command fails if DirectX is not installed If D3D7Max2DDriver() Or D3D9Max2DDriver() Then DebugInt=113 DesktopRefreshRate = GetDeviceCaps(GetDC(0),VREFRESH) EndIf ? ... 'Get GameRefreshRate, may not be same as DesktopRefreshRate in full-screen mode GameRefreshRate = -1 'means value not read DebugInt=136 ?Win32 'This command fails if DirectX is not installed If D3D7Max2DDriver() Or D3D9Max2DDriver() Then DebugInt=137 GameRefreshRate = GetDeviceCaps(GetDC(GetActiveWindowSafe()), VREFRESH) EndIf ? Method GetActiveWindowSafe%() ... If Driver = 0 Then Local my_driver:TD3D7GraphicsDriver = D3D7GraphicsDriver() Local my_graphics:TD3D7Graphics = my_driver.Graphics() 'If the my_graphics is null, use DX 9 driver instead. If my_graphics <> Null Then Return my_graphics._hwnd Else Local my_driver:TD3D9GraphicsDriver = D3D9GraphicsDriver() Local my_graphics:TD3D9Graphics = my_driver.Graphics() Return my_graphics._hwnd EndIf I think thats it... Oh and I did have to add this: Method Graphics:TD3D9Graphics() Return _graphics End Method To dxgraphics.mod\d3d9graphics.bmx.... good luck ;) Last edited 2012 |
| ||
Small bug. This line near the bottom: If D3D7Max2DDriver() Or D3D7Max2DDriver() Then should be: If D3D7Max2DDriver() Or D3D9Max2DDriver() Then |
| ||
Opps - Thanks Jake ;) I've updated the above code :) Last edited 2012 |
| ||
Also instead of this:SetGraphicsDriver D3D9Max2DDriver() DriverDX9 = 1 I call this: Method SetDXDriver(DX7:Int) ?Win32 If DX7 Then DriverDX7 = 1 DriverDX9 = 0 SetGraphicsDriver D3D7Max2DDriver() Else DriverDX7 = 0 DriverDX9 = 1 SetGraphicsDriver D3D9Max2DDriver() 'Ideally there would be some code here to check if DX9 wasn't used and DX7 was so the DriverDX7 flag could be set. 'Oh wait, there is now! If Not D3D9Max2DDriver() DriverDX7 = 1 DriverDX9 = 0 SetGraphicsDriver(D3D7Max2DDriver()) 'This may mean that DoSetVirtualResolution() fails due to DX7, but the small netbooks it required on are all DX9 anyway. EndIf EndIf ? End Method Last edited 2012 |
| ||
No, thank you, because I updated my code with some of yours (like I wasn't testing for DX9 driver in a few places.) Last edited 2012 |
| ||
You also might want to update DebugPrint a bit, I found this useful for testing:'Drivers and memory If Driver = 0 Then Local using:String = " (using DX" If DriverDX7 Then using = using + "7)" If DriverDX9 Then using = using + "9)" C.OutputNL("Gfx Drv " + "DirectX " + DirectXVersion + using) Else C.OutputNL("Gfx Drv " + "OpenGL") EndIf |
| ||
Yep I've got something like that too... but it was getting a bit of a mess posting all that code...If Driver = 0 Then If DriverDX9 Then C.OutputNL("Using DirectX9") C.OutputNL("Gfx Drv " + "DirectX " + DirectXVersion) Else C.OutputNL("Gfx Drv " + "OpenGL") EndIf If WideScreen = 1 Then C.OutputNL("Using Widecreen") Else C.OutputNL("NO widescreen") EndIf C.OutputNL("DesktopWidth " + DesktopWidth) C.OutputNL("DesktopHeight " + DesktopHeight) C.OutputNL("ScreenWidth " + ScreenWidth) C.OutputNL("ScreenHeight " + ScreenHeight) C.OutputNL("WindowWidth " + WindowWidth) C.OutputNL("WindowHeight " + WindowHeight) If UsingVirtualResolution Then C.OutputNL("UsingVirtualResolution = True") Else C.OutputNL("UsingVirtualResolution = False") End If ... C.OutputNL("Mouse Lag Fix " + lagFixOn + " F1 to toggle") Last edited 2012 |
| ||
Nice, I've just added those virtual res related ones. Still haven't added hardware cursor or widescreen support yet, it's on my to do list (have bookmarked this thread). Working on another main project at the moment for someone else with a team and need to crunch. Last edited 2012 |
| ||
Cool! BTW I've just tested Spring Bonus on this work PC and I can see the slow down with the lagfix in full screen mode, its playable but I can tell its not running at 60FPS. |
| ||
OUCH. Well worth knowing thanks. That PC isn't a bad spec right (I didn't know if the video card was good/bad)? So it's like I could leave off lagfix and maybe there'd be a small lag but at least the game would be 60FPS smooth and still offer the ability to switch off the custom cursor for people who hate the lag. I wonder how many sales I lost due to lag appearing bad? It actually converted the best of all my match-3s on BFG and I didn't see any complaints. I have heard a few people on BFG moaning that custom cursors (in other games) are REALLY slow, like awful, and BFG QA confirmed this, so not just a few frames lag, like tons. Could be a DX9 issue where the buffering is extreme or something. That's why I added the option in to not use a custom cursor at all. Could retro fit it to my old games if I could be arsed :-) Last edited 2012 |
| ||
Hey therevills, I added Widescreen to Spring Bonus using the code above and it works well thanks. Couple of weird things I noticed though: - The mouse goes off the right hand side of the screen into the black border. Did you limit the mouse X coord in your framework somehow (I noticed it doesn't go to far right in your latest solitaire game)? - Something weird is going on with the left hand side of the screen. If I move the mouse cursor over there its tip is able to draw in a 2 pixel column on the black border to the left of the main background and also any dialogs that slide on/off leave a trace in that thin column. In your solitaire game your mouse cursor doesn't quite reach the left side (mouse offset?) and so you can't see the same effect, BUT I think there is a thin strip down the left side that is not the right colour when you go from title to options for example. Check it out. |
| ||
OK found the problem with the left side. It's a rounding error on vxoff, vyoff because SetViewPort uses ints and SetOrigin uses floats. So try this: SetViewport ccRound(vxoff), ccRound(vyoff), ScreenWidth, ScreenHeight SetOrigin ccRound(vxoff), ccRound(vyoff) It makes them consistent. Last edited 2012 Last edited 2012 |
| ||
One more thing. I notice you are resetting vxoff and vyoff to zero in ToggleWidescreen. Why is that? If they are not used anywhere else in the code except DoSetVirtualResolution() they might as well be local to DoSetVirtualResolution() which is what I've done (declared as floats) Last edited 2012 |
| ||
Ah crap, turns out that fix only worked on the PC that had 1600x900 resolution (for my 1024x768 game). On my 1920x1200 PC the problem still exists on the left border. I expect I could fix it by drawing over it every frame but that seems a bit lame. Probably the solution is some careful rounding of some of the other variables. I already tried making them all Double but that didn't work. [edit]Hmm, after some more experimentation I'm thinking the solution may be to Floor sx and sy like this as well as keeping the rounding from my previous post, but I'd love a second opinion. SetVirtualResolution Floor(sx), ScreenHeight ... Having some more thoughts now, I wonder if sx/sy should always be rounded to an EVEN number such that sx - ccRound(vxoff)*2 = ScreenWidth. Last edited 2012 |
| ||
Its been quite awhile since I looked at this... I notice you are resetting vxoff and vyoff to zero in ToggleWidescreen. Why is that? If they are not used anywhere else in the code except DoSetVirtualResolution() Looks like I now use them in DoMoveMouse: Method DoMoveMouse(x:Int, y:Int) 'This correct scales the coords if a virtual resolution is being used. If UsingVirtualResolution = 1 'This is untested if Full Screen is bigger than windowed e.g. 1024x768 fullscreen and only 800x600 in windowed due to Desktop res of 1024x768. MoveMouse ((x + vxoff) / VirtualRatioX) , ((y + vyoff) / VirtualRatioY) Else MoveMouse(x, y) End If End Method And UpdateCoords: Method UpdateCoords() 'Uses Windows API call to get exact coords relative to desktop. 'Doesn't use MouseX() and MouseY() as they rely on PolledInput. 'Also doesn't use event-based updates because they would only update X/Y 'if a MouseMove event was received since PollEvent() was last called (unlikely). Local pt:TPoint ?Win32 pt = ccGetCursorPos() 'This is position relative to the top left of the desktop ? ?MacOS pt = ccGetCursorPosOSX() 'On Macs the mouse Y coord is reversed (0 is at the bottom of the screen) 'So I need to reverse it based on the Desktop Height. pt.Y = Game.DesktopHeight-pt.Y ? If pt <> Null Then 'Use client area position so we can see if the cursor is outside of window. Game.SetClientCoords() 'Need this in case window has been dragged as drag code doesn't alway set it soon enough. x = pt.x - Game.ClientX y = pt.y - Game.ClientY If UsingVirtualResolution Then x = ccRound(x * VirtualRatioX) - Game.vxoff y = ccRound(y * VirtualRatioY) - Game.vyoff EndIf EndIf End Method And DoPeekEvent: Case EVENT_MOUSEMOVE If UsingVirtualResolution Then Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff Mouse.y = ccRound(EventY() * VirtualRatioY) - vyoff Else Mouse.X = EventX() Mouse.Y = EventY() End If Also just to let you know I had to actually disable the Widescreen support for Mac, on my Mac it worked fine but BFG found on some it didnt display correctly. Looking at the screen shot they sent me, I think the viewport doesnt seem to be "cutting" correctly or something: ![]() And since I couldnt reproduce it I just disabled that option for Mac within the game. Last edited 2012 |
| ||
Thanks for the info. Hmm guess I'd better disable it on Mac too. I've never heard about viewports failing on Mac before. Wonder if it's videocard specific? So are you clamping the mouse on the right hand side of the screen somehow as I notice yours doesn't go into the black border on the right (it does on mine)? EDIT: OK I just added vxoff and vyoff to my other code and now the mouse goes off both the left and the right by equal amounts (instead of only off the right). Weird. Also what are your thoughts about avoiding the thin column on the left that seems to get drawn on when it shouldn't? I've tried a bunch of things and think I have a solution. Will post code later. Last edited 2012 |
| ||
OK here's what I did in the end. I discovered that if sx-vxoff*2 = ScreenWidth, sometimes you can't see the edge pixels on the right hand side. So I've deliberately shrunk it down a bit more to the nearest even number which seems to work.Local sx:Float = Float(ScreenWidth) * gtovratio 'Round sx down to the nearest even number that will look a bit smaller than the ScreenWidth required. Local finalsx:Int = Floor(sx-1) If finalsx Mod 2 = 1 Then finalsx:-1 SetVirtualResolution finalsx, ScreenHeight vxoff = (gwidth - pixels) * half_scale vyoff = 0 Print sx Print finalsx Print vxoff Print finalsx-ccRound(vxoff)*2 SetViewport ccRound(vxoff), vyoff, ScreenWidth, ScreenHeight SetOrigin ccRound(vxoff), vyoff Need to do the same for sy and vyoff Last edited 2012 |
| ||
btw, I did some testing and I don't think you need vxoff and vyoff in UpdateCoords() because it is only called in windowed mode and of course widescreen is not turned on in windowed mode so vxoff and xyoff =0. Mind you it doesn't harm to have it in there. |
| ||
Here's what I did in the end to clamp the mouse in DoPeekEvent:... Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff Mouse.Y = ccRound(EventY() * VirtualRatioY) - vyoff 'It's possible for the mouse to go offscreen in widescreen mode, so we need to do some clamping. If FullScreen And Widescreen Then If Mouse.X<0 Then Mouse.X=0 DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Mouse.Y<0 Then Mouse.Y=0 DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Mouse.X>=ScreenWidth Then Mouse.X=ScreenWidth-1 'May cause a slight jiggle on the right side. DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Mouse.Y>=ScreenHeight Then Mouse.Y=ScreenHeight-1 DoMoveMouse(Mouse.X,Mouse.Y) EndIf EndIf ... |
| ||
Yep, Ive got similar code in DoPeekEvent to handle the mouse:Case EVENT_MOUSEMOVE If UsingVirtualResolution Then Mouse.X = ccRound(EventX() * VirtualRatioX) - vxoff Mouse.y = ccRound(EventY() * VirtualRatioY) - vyoff Else Mouse.X = EventX() Mouse.Y = EventY() End If ' this stops the custom cursor going offscreen when using the virtual res... If FullScreen If Self.Mouse.useSystemPointer = 0 If Mouse.x < 0 Mouse.x = 1 ' 1 instead of 0, because it would hang!? DoMoveMouse(1, Mouse.y) End If If Mouse.x > ScreenWidth Mouse.x = ScreenWidth - 1 DoMoveMouse(ScreenWidth - 1, Mouse.y) End If If Mouse.y < 0 Mouse.y = 1 DoMoveMouse(Mouse.x, 1) End If If Mouse.y > ScreenHeight Mouse.y = ScreenHeight - 1 DoMoveMouse(Mouse.x, ScreenHeight - 1) End If EndIf EndIf |
| ||
Ha! almost the same :-) |
| ||
Found a couple of bugs in my implementation that may affect you: 1) the global vxoff and vyoff must be set to 0 at the start of DoSetVirtualResolution() just in case they were set previously (due to full screen widescreen mode being used) and then you go back to windowed mode in which widescreen isn't being used. If you don't do that you get some bad mouse sync issues. 2) In ToggleWideScreen() you should ALWAYS call DoSetVirtualResolution(), not just if the game is in fullscreen. This is because if you are in windowed mode and it is scaled down on a low resolution laptop, and you toggle widescreen on/off, when it goes off it will turn off UsingVirtualResolution in your code sample and never turns it back on again (via DoSetVirtualResolution) which is bad news because it should be on. This causes the mouse to go out of sync. Hope those make sense. |
| ||
@therevills: Figured out what was causing the mouse repositioning to rehang in rare cases when using widescreen. Here's my code:'It's possible for the mouse to go offscreen in widescreen mode, so we need to do some clamping. 'But *only* for the custom cursor as it looks bad with the windows pointer (it springs back into position) If FullScreen And Widescreen And CustomMousePointer Then Local FloatX:Float = (EventX() * VirtualRatioX) - vxoff Local FloatY:Float = (EventY() * VirtualRatioY) - vyoff 'Calc edge coords using virtual ratio, otherwise the cursor can sometimes get stuck with the following code. Local LeftEdge:Int = 0 Local TopEdge:Int = 0 Local RightEdge:Int = ScreenWidth-1 Local BottomEdge:Int = ScreenHeight-1 'Need to use Ceil and Floor to stop mouse getting stuck due to infinitely repositioning due to being off by a fraction of a pixel. 'This may result in the cursor jiggling at some edges in some resolutions. If Ceil(FloatX)<LeftEdge Then Mouse.X=LeftEdge DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Ceil(FloatY)<TopEdge Then Mouse.Y=TopEdge DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Floor(FloatX)>RightEdge Then Mouse.X=RightEdge DoMoveMouse(Mouse.X,Mouse.Y) EndIf If Floor(FloatY)>BottomEdge Then Mouse.Y=BottomEdge DoMoveMouse(Mouse.X,Mouse.Y) EndIf EndIf There's probably a better way of solving this (without the possible 1 pixel jiggle) but it's been driving me crazy trying to figure it out, so for now this will do. Last edited 2012 |
| ||
Also I was still getting issues with a column of pixels not being cleared on the side so I updated the code to have a slightly smaller viewport and that has sorted it (I hope):If graphicsratio >= virtualratio 'Desktop is wider than game screen size OR the same Local vscale:Float = Float (gheight) / Float(ScreenHeight) 'How many desktop pixels are used per virtual pixel. e.g. 2.0 for an 800x600 game screen on a 1920x1200 desktop Local pixels:Float = Float(ScreenWidth) / (1.0 / vscale) 'Width in desktop pixels based on game screen scaled up so height matches desktop height. Local half_scale:Float = (1.0 / vscale) / 2.0 'This is half of how many virtual pixels are represented by desktop pixels. e.g. 0.25 for an 800x600 game screen on a 1920x1200 desktop 'Work out many pixels wide (at current resolution set by Graphics()) we have to pretend the screen is 'so that the middle part that we want to draw to will be squashed to the correct aspect ratio. Local sx:Float = Float(ScreenWidth) * gtovratio 'Round sx down to the nearest even number that will look a bit smaller than the ScreenWidth required. Local finalsx:Int = Floor(sx-1) If finalsx Mod 2 = 1 Then finalsx:-1 SetVirtualResolution sx, ScreenHeight 'Calc the leftmost pixel to start drawing at ' vxoff = (gwidth - pixels) * half_scale vxoff = (finalsx-ScreenWidth)/2 'More accurate that method on line above. vyoff = 0 'Make the viewport a little bit smaller than the virtual resolution to avoid a column of pixels on the edge which is not cleared. SetViewport ccRound(vxoff)+1, vyoff, ScreenWidth-2, ScreenHeight SetOrigin ccRound(vxoff), vyoff VirtualRatioX = sx / Float(WindowWidth) VirtualRatioY = ScreenHeight / Float(WindowHeight) UsingVirtualResolution = 1 Else ... Last edited 2012 |