One of the best (and worst) things about SVG is that it has its own coordinate system. It’s the best because it’s not confined by the HTML coordinate system and worst because it doesn’t always play well with its HTML cousin.
The biggest issue I had was trying to figure out how to transfer the coordinates of the laser fire from the HTML DOM to the coordinates of the SVG I was firing at so that I could use those isPointInStroke and isPointInFill functions. Because the coordinates of the click that happened in the DOM would have no meaning once we crossed from the DOM/HTML coordinate system to the SVG coordinate system.
Luckily, I stumbled upon this article that showed me how to handle both coordinate systems. https://www.sitepoint.com/how-to-translate-from-dom-to-svg-coordinates-and-back-again/ The upshot of that article is that you create an SVG point (pt) using the aptly named createSVGPoint(), set pt’s X and Y coordinates from the DOM point, then use these functions to get the coordinates in the SVG (where svgelement is the <svg> element you’re translating the coordinates into):
pt.matrixTransform( svgelement.getScreenCTM().inverse() );Code language: CSS (css)I could then translate units from the DOM (where the user clicked) to SVG (the enemy, missile, letter, power-ups, etc. ) even though I was translating and scaling the enemies, missiles, power-ups, etc. The matrixTransform/getScreenCTM handled everything for me, and so I was shooting out missiles and enemies and power-ups in no time.
However, whenever I was playing in Safari, I could click the missiles right on the nose, the cannons would fire, the lasers would visually hit the missiles and the missiles wouldn’t explode. I tried all kinds of things to get it to work, but nothing would fix it. I even set up a system where I would click and it would set a small circle in the SVG so that I could compare where I clicked with where it registered. They were not the same location.
The issue was most pronounced with the missile. Missiles are “sent” from an enemy, starting small (scaled down) and getting larger (scaling up) as they get “closer”. Eventually, I figured out that Safari does not seem to take scale into account when running the getScreenCTM function, meaning the coordinate conversion was not accurate.
I again tried a bunch of fixes for this, but no contortions of the coordinate conversion function worked. While making one site that works on all browsers is ideal (browser chasing is often a fool’s errand), the differences were so egregious that the Safari version didn’t really work. Worse, its behavior was frustrating, because it seemed like it worked until it didn’t, often right before the missile exploded on you.
Thus, my best bet for sidestepping insanity was to detect Safari and use a special collision detection algorithm just for it. In Safari, collision detection in the game portion is done using overlapping bounding boxes. If the point fired at it is within the bounding box of the enemy or the missile (just the main barrel of the missile, not the fins) then it counts as a collision and the enemy or missile is killed/exploded. While this adds some space, particularly on some enemies (“bugs” for instance), where you could shoot empty area and still kill the enemy or missile, those spaces are pretty small and the new behavior favors the player, making them far less likely to be frustrated. However, I still use the isPointInFill and isPointInStroke for the letters in Safari, because as discussed in Part 3 using the bounding box to determine a hit on them would lead to some rather far off shots taking out those letters instead of near accurate shooting.
