In some ways, uncompressed levels could be the simplest thing that could possibly work.
Assuming you base collision properties off tile ID:s, collision becomes very direct. For instance, it is safe to assume that a blank tile at position $00 in the tile space is always a noncollision, so you can skip out testing this majority case with a bne and continue with the rest. then, if you've sorted your tiles according to their collision properties, you can check for ranges of tile ID:s.
Different attributes can even have overlapping ranges, if you use a method like so:
.macro getInRange lo, hi
;expects value in A
;returns carry if in range
;a is not preserved
; 6 cycles, 5 bytes
; other assumptions:
; lo must be higher than 0.
; hi must be higher than lo.
; hi must be lower than #$ff
clc
adc #$ff - hi
adc #hi - lo + 1
.endmacro
of course, the biggest problem with uncompressed levels are the sheer size - just one full screen = 1kB. A typical homebrew cartridge might hold 512kB since that costs roughly the same as any smaller size, so there is space. But it's not endless.