![]() |
SUBSIM: The Web's #1 resource for all submarine & naval simulations since 1997 |
![]() |
#1 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
For some time now, I was wondering if I am able to do some modding for SH5. Since currently my time capacity (nevermind any artistic talent, or rather lack of it) does not allow me to work on something of considerable size, I've decided to start small, with tools.
In one of his posts in NewUIs thread, TheDarkWraith stated that mouse input in SH5 is a mess, and I totally agree with that statement. The whole UI scripting is cumbersome and documentation is, well, lacking. Nevertheless, I always wanted to have in-game tools such as attack disc or RAOBF behave just as they did in, say, SH3: i.e. so that I could click my mouse and rotate those rings to my hearts content. And to be honest, my middle finger just got tired of rotating the mouse wheel when I'm using NewUIs ![]() Interestingly enough, API provided for UI elements allows adding drag/resize handles for items, but these are just not enough for situations when I would have a complex object, such as RAOBF. Here, I want to share what I was able to find out. So, I started to dig around and see how exactly point-click-drag can be implemented. Well, basics are obvious: I just need to periodically poll mouse state with that 'animation' timer whilch I'm sure is familiar to most modders. Thus I can emulate mouse motion events and handle dragging. So, I've quickly coded a small class (in C#) that can help with this task. It's called InputCanvas, and it's only purpose is to emulate mouse motion events for FocusMenuItemWrapper, the main UI element class that can receive mouse input (non-focusable elements receive only mouse-in and mouse-out events). Incidentally, it also forwards mouse press/release/click events, just for ease of coding Python scripts. But then things get trickier. Say I want to implement front side of the attack disc, with all 4 elements draggable by mouse. This means that I'll have 4 overlaying images (not counting outer relative bearing scale, which is static), for which I need to implement rotation via dragging. And of course these images are not just rectangles, they are mostly circles with customary pointers attached to them. But by default, game treats the whole image as an input area, therefore I need some way to determine which image I actually click: in case of attack disc, images are positioned 'on top' of one another, and though some areas of top-level image are transparent, they would still receive mouse input (though I visually click on the image 'below'). Well, everybody saw the attack disc ![]() While I was familiarizing myself with API, for several times I encountered a mention of 'touchpads'. As it turned out, you can specify the shape of input area for MenuItemWrapper by adding those touchpads. Once at least one touchpad is added, the game stops treating UI element as a rectangle, and processes input only in allowed area. There are two main types of touchpads. One allows input (MenuItemWrapper.InsideTestModes.Inside) and one denies it (MenuItemWrapper.InsideTestModes.NotInside). As far as I can tell, you can add any number of touchpads to the item, and game will combine them all to determine if mouse input is actually allowed for a particular area of the item. There are three possible 'shapes' for a touchpad: an ellipse (ideal for circular dials and buttons), a triangle (e.g. TAI maximize/minimize button) and an arbitrary polygon (which I think game just splits up to multiple triangles). I made a quick test with ellipses (attached one to each of attack disc parts), and my attack disc suddenly started to respond to dragging its elements. It was first 'hurray' for me. Though before that, I had a small issue in determining what do parameters for AddTouchPadEllipse() actually mean, but more on that later. Anyway, some more quick tests quickly showed me that I can actually define any custom shape for the input area: it's only a matter of determining the coordinates for touchpads. And so I patiently calculated all the coordinates for each pointer on the attack disc, added a corresponding touchpad polygon, started my game and... well, was surprised. For some reason, game refused to react on my attempts to drag a pointer. And the Attack Course pointer started to behave very strange when I rotated it (when it was horizontal I had a hard time finding a place which allowed me to drag it). Something was definetly going on. Luckily, I found out what rather quickly. Documentation states that changing Rotation property for MenuItemWrapper (i.e. rotating it) also rotates all touchpads. What it doesn't say is that rotations are confined to item's bounding box. If it is rectangular, polygons start to shrink or extend when rotated. And several images from my attack disc (I use the one from TheDarkWraith's NewUIs) are indeed rectangular. Well, I thought OK, how about I put each disc in a separate control group, make these groups square and rotate them instead. Unfortunately, the game refused me this attempt, stating that it cannot rotate items without a texture (and control groups are just that). So I had to resize all the textures to make them square, recalculate my touchpads and try again. And it worked: all the discs rotated correctly. This was the second 'hurray'. The only issue I have with this is that I had to make images larger, therefore now they consume more VRAM. Though I don't know if it can become a significant problem, considering the overall ratio of UI to game world texture usage (and anyway, square images are only needed if you intend to rotate them). I promised to tell about parameters for funcions that add touchpads. Here goes: For all functions, IfInside parameter sets the type of the touchpad: including (allows input) or excluding (denies input) Code:
AddTouchPadEllipse( IfInside, CenterXPercent, CenterYPercent, WidthPercent, HeightPercent ) CenterXPercent = 0.5 CenterYPercent = 0.5 WidthPercent = 1.0 HeightPercent = 1.0 This is important to remember when calculating actual size for this type of touchpad. Code:
AddTouchPadTriangle( IfInside, x0, y0, x1, y1, x2, y2 ) AddTouchPadPolygon( IfInside, Single[] coords ) One last thing to note about touchpads: AFAICT, they can only be added, but not removed, and they are not removed when page's script is unloaded. This is a bit unfortunate, but I personally can live with that. To summarize: while somewhat problematic, it's actually not that hard to implement custom-shaped widgets. With taking several quirks I mentioned into account, it becomes only a matter of calculating actual touchpad areas. This is my second step, as I'm now starting to work on application that will allow to manually "draw" touchpad areas for textures and save them into text file, so that it can be read inside script during initialization. Another approach would be to use some form of edge detection and vectorization to calculate the input areas automagically, but that's beyond my abilities for now. If anyone is interested, I've uploaded my InputCanvas class along with sample Python module. Maybe it'll help someone in creating another awesome mod. Simply place the dll into SH5 installation folder and use it like this: Code:
import clr clr.AddReference("RadCapTools") from RadCapTools import InputCanvas Download here I've recorded a short video to illustrate what all the above rant means. . I hope whoever would watch would also excuse my English ![]() Cheers ![]() Last edited by radcapricorn; 06-01-12 at 05:58 PM. |
![]() |
![]() |
![]() |
#2 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
Okay, for all UI modders who want to utilize the touchpads functionality right now, here's the how-to. Beware it's a bit technical!
Also note, the how-to does not use the sample python script provided in the download, for that wait for a further example.
Now you may ask, then what the hell this AttackDisc.py in sample downloads is for? Well, this how-to I built from scratch. And that script I extracted from my actual testing sandbox. It's basically just a more sophisticated version of code presented in this how-to, and it handles the full attack disc. If you look carefully at that code, you'll find that it's pretty similar to the one presented here, it only differs in function signatures (since AttackDisc is a class there) and in amount of InputCanvas objects. I put in the sample just what I exctracted into one neat class so that I don't clutter my own functions with it. In fact, I already figured out how to actually use it as a separate script file (i.e. scripts/menu/radcapricorn/AttackDisk.py) without creating any dummy pages, but that's another topic. If I get permission from TheDarkWraith to post changes to his scripts, I'll show you how you can use that sample python module to actually control attack disc in TDW's UI. I've actually already tested that code with his UI (since I already have touchpad areas calculated for that attack disc) and I must say it works perfectly. For any other objects (RAOBFs, 3-bearing tools and whatnot) you'll have to wait until I've finished my calculator app: I'm done with manual calculations ![]() As a side note, you don't need to use my InputCanvas if you just want to create shaped buttons. Touchpads are enough for that. See how triangle buttons are done in stock Page Default Hud.py, for example. You can use the same approach to make round buttons, hexagonal ones, you name it. There are limitations, of course, but I'll post them once I've tracked down at least all major ones. Wow, that took a lot more time than I expected. But that's not all. Stay tuned ![]() Have fun! Last edited by radcapricorn; 06-01-12 at 05:58 PM. |
![]() |
![]() |
![]() |
#3 |
sim2reality
![]() Join Date: Jun 2007
Location: AM 82
Posts: 2,280
Downloads: 258
Uploads: 30
|
![]()
This is amazing, well done - this is one thing I solely missed making my UI
![]() Wii add this to my UI Ui-boat V5. Ps The RAOBF are from my Ui-Boat V5 and also HAHD U-Boot_HAHD ![]() Nice to see it working the way I always dreamed it to work ![]() Last edited by reaper7; 06-01-12 at 07:48 AM. |
![]() |
![]() |
![]() |
#4 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
Thanks, reaper7! I was beginning to think nobody's interested
![]() I still have to insist that this RAOBF I took from CSP MaGui by dr. Jones ![]() See the outer ring: it has this metallic rod thing at 8 o'clock, and a plastic 'cover' with red mark at 1 o'clock. These actually (I guess) should block the input to the inner ring (they are a part of the outer ring after all), but as of now such fancy stuff is not possible to do without adding (dummy) items, as I encountered some issues with overlapping input areas inside one UI item (I'll investigate a bit more and update the first post accordingly). IIRC, in your mod the plastic markers are in a separate image, so this should not pose any problems at all. PS. I beleive I've also answered your PM, but right now my 'sent items' box is empty. I only guess it's due to ongoing maintenance, so I'll wait and see if it pops up. |
![]() |
![]() |
![]() |
#5 |
Navy Seal
![]() |
![]()
Hi radcapricorn
![]() I have just watched your vid for the mouse control and it looks great ![]() With the download, if I am using TDWs UI and install the .dll in Sh5 main dir and the .py script in the script/menu file - will i be able to control the RAOBF and attack disc as you did in the vid ![]() |
![]() |
![]() |
![]() |
#6 | |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
Thanks, Trevally!
Quote:
![]() For example, here's how I do it in my test page (simplified code off the top of my head): Code:
# File scripts/menu/pages/Page_Test1.py # The following is the contents of the sample script import clr clr.AddReference("RadCapTools") from RadCapTools import InputCanvas class AttackDisc( object ): # The definition of the attack disc from the sample script follows # ... # The contents of sample script end here # global variable to store the attack disc input handler g_AttackDisc = None def InitializeScript(): compassRose = Page_Test1_AttackDisc_Front_CompassRose bearingLead = Page_Test1_AttackDisc_Front_BearingLeadAngle targetDisc = Page_Test1_AttackDisc_Front_TargetDisc attackCourse = Page_Test1_AttackDisc_Front_AttackCourse # Create the attack disc input handler global g_AttackDisc g_AttackDisc = AttackDisc(compassRose, bearingLead, targetDisc, attackCourse) # Some more code skipped... def UnloadScript(): # Destroy the attack disc input handler global g_AttackDisc if g_AttackDisc: g_AttackDisc.dispose() g_AttackDisc = None Now, those Page_Test1_AttackDisc_Front_* names are for my UI page. For TDW's, you'll have to look at data/menu/pages/Page TDC.ini to figure out proper names. I think I may have to write a more detailed instructions on what to do, but that'll be later once I'm at my SH5 rig and have an actual UI mod files to use for illustration. Oh, yeah, and a follow-up: In the first post, I mentioned the issues with rotation if the textures are not square. Some original attack disc textures from TDW's UI are not square so I had to edit them accordingly (this required resizing them in GIMP and making changes in SH5 Menu Editor). Anyway, excpect an update with proper instructions in an hour or so. Last edited by radcapricorn; 06-01-12 at 01:49 PM. Reason: Follow-up on rotations |
|
![]() |
![]() |
![]() |
#7 |
Navy Seal
![]() |
![]()
Thanks radcapricorn
![]() |
![]() |
![]() |
![]() |
#8 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
Post #2 is updated with a small how-to.
|
![]() |
![]() |
![]() |
#9 | ||
Navy Seal
![]() |
![]() ![]() great tutorial ![]() Quote:
![]() Quote:
![]() ![]() |
||
![]() |
![]() |
![]() |
#10 | ||
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]() Quote:
![]() Quote:
|
||
![]() |
![]() |
![]() |
#11 | |
Ocean Warrior
![]() |
![]() Quote:
__________________
. Where does human stupidity end? . ![]() ![]() El sueño de la razón produce monstruos © - and for some people awakening will be cruel |
|
![]() |
![]() |
![]() |
#12 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]() ![]() |
![]() |
![]() |
![]() |
#13 |
sim2reality
![]() Join Date: Jun 2007
Location: AM 82
Posts: 2,280
Downloads: 258
Uploads: 30
|
![]()
Thanks to radcapricorn I now have a fully working Attack Disc in my next release of Ui-Boat
![]() ![]() |
![]() |
![]() |
![]() |
#14 |
Helmsman
![]() Join Date: Jun 2011
Posts: 105
Downloads: 181
Uploads: 0
|
![]()
You are welcome!
![]() |
![]() |
![]() |
![]() |
|
|