Hashs sind schon die richtige Idee, aber ich würde keine Hashs mit einem [x,y] Key verwenden. Besser wäre ein Hash in der Form eines Gitternetzes. Du hast ja vorher die Idee einer Tabelle geäußert, bei der jedes 4x4 Tile einen Tabelleneintrag hat. Du musst aber nicht jedem Tile einen Tabelleneintrag geben. Du kannst die Tabelle auch gröber machen. Stell sie dir wie ein grobmaschiges Netz vor, dass du über die Karte spannst. Es könnte z.B. 64x64 große Gitterfelder haben, oder noch größer, je nachdem wie du zwischen Speicher und Performance ausbalancieren willst. Solch ein Gitternetz hat noch einen weiteren Vorteil: Du kannst nicht nur effizient Kollisionen überprüfen, sondern auch effizient ob ein Event in der Nähe eines anderen ist (goldwert, wenn du sowas wie "Sichtweite, oder Reichweite von Waffen o.ä. einführen willst). Denn dann musst du nicht jedes Nachbarfeld absuchen, sondern nur noch Events die sich innerhalb der benachbarten großen Gitterfelder aufhalten (was bei dünnbesiedelten Maps WESENTLICH weniger Aufwand ist).
Wie könnte so eine Tabelle aussehen?
Hier mal der Rohbau für einen 2D-Array:
Damit hast du schon mal eine einfache 2D-Array Konstruktion.
Jetzt kannst du davon eine Subklasse für eine GridMapHash Klasse machen:
Jetzt überschreibst du noch die movement-Methoden der Game_Character Klasse und sagst den Events, dass sie alle Positionsänderungen an diese GridMapHash Instanz melden sollen. DIe Methode is_blocking? musst du noch selbst schreiben. Die sollte prüfen ob das Event dem Char wirklich im Weg steht.
Edit: Ich hab noch was übersehen. Du verwendest Kollisionsbereiche für deine Events. Das hat natürlich den Nachteil, dass ein Event zwar im Gitterfeld x/y stehen kann, sein Kollisionsbereich aber ins Feld x+1/y hineinragt. Entsprechend musst du die field_blocked? Methode umschreiben, so dass sie auch die umliegenden Felder überprüft, die im Kollisionsradius liegen. Wenn du die Auflösung größer als den größtmöglichen Kollisionsradius wählst, werden immer maximal 3 Gridfelder überprüft werden müssen. Der Aufwand hält sich dann also trotzdem in Grenzen. Es ist nur eben etwas mehr Programmieraufwand das hinzuschreiben.