Just because you can put any data on a blockchain, doesn't mean you should. On fee-based chains, the cost to upload your data can quickly get very expensive. Especially for large collections of many large files like NFT images. This is why most NFTs use IPFS or other off-chain storage for the actual images.
The smart contract for the NFT just needs to store a URL for the image so that it can be displayed on your website, app, or an NFT marketplace.
To be clear, there's nothing stopping you from putting whatever data you want on a blockchain. Blockchains are inherently inefficient. To do anything or store anything on blockchain requires lots of computers to run the same code and store the same data. Even for a fee-less chain, the cost is still real compute time, real storage, real bandwidth, and real electricity to power it all. The economics of the chain don't change the scalability.
So when it comes to complex dApps like on-chain games with lots of micro-interactions every time the game is played, the data can get out of hand quickly. Most of the time, once a match or round of a game is over, that data isn't needed. You really only need to keep track of the final result.
Fortunately, koinos has a very effective feature for games like this called state-paging. You can put your game state on-chain, then page it out without sacrificing the trustlessness of the final result. You can later recover the state from off-chain storage if you really need it.
This is a big step up, but it's still not a silver bullet. You shouldn't store just any data on-chain. It's important to consider the pros and cons of various storage options before deciding to use the blockchain for everything in your app. For many dApps, the blockchain should probably only be used for core metadata.