query_posts works while get_posts doesn’t

Every page has a “main query” which is run before the template is loaded. The results of the main query are how WordPress determines what template to load. The standard Loop operates on the data contained in the main query, this is why it seems to just magically “work” without you having to explicitly query something yourself.

query_posts overwrites the contents of that main query. This is why you shouldn’t use query_posts– you’re changing the query results after the template is loaded, which can have unexpected consequences. pre_get_posts is the preferred way to modify the main query, it runs before the main query happens, so that everything that follows is operating on the correct set of data.

get_posts and WP_Query are for running additional queries, separate from the main query. You have to assign the results to a variable, and run some sort of loop on those results. Your use of get_posts is doing nothing, because you’ve done nothing with the results of that query.